diff -uprN linux-2.4.25.old/Documentation/Configure.help linux-2.4.25/Documentation/Configure.help --- linux-2.4.25.old/Documentation/Configure.help 2004-06-26 11:22:00.000000000 +0100 +++ linux-2.4.25/Documentation/Configure.help 2004-06-26 11:29:29.000000000 +0100 @@ -6204,6 +6204,57 @@ CONFIG_IPV6 It is safe to say N here for now. +IPv6: IPv6 over IPv6 Tunneling (EXPERIMENTAL) +CONFIG_IPV6_TUNNEL + Experimental IP6-IP6 tunneling. You must select this, if you want + to use CONFIG_IPV6_MOBILITY. More information in MIPL Mobile IPv6 + instructions. + + If you don't want IP6-IP6 tunnels and Mobile IPv6, say N. + +IPv6: Mobility Support (EXPERIMENTAL) +CONFIG_IPV6_MOBILITY + This is experimental support for the upcoming specification of + Mobile IPv6. Mobile IPv6 allows nodes to seamlessly move between + networks without changing their IP addresses, thus allowing them to + maintain upper layer connections (e.g. TCP). Selecting this option + allows your computer to act as a Correspondent Node (CN). A MIPv6 + Mobile Node will be able to communicate with the CN and use route + optimization. + + For more information and configuration details, see + http://www.mipl.mediapoli.com/. + + If unsure, say N. + +MIPv6: Mobile Node Support +CONFIG_IPV6_MOBILITY_MN + If you want your computer to be a MIPv6 Mobile Node (MN), select + this option. You must configure MN using the userspace tools + available at http://www.mipl.mediapoli.com/download/mipv6-tools/. + + If your computer is stationary, or you are unsure if you need this, + say N. Note that you will need a properly configured MIPv6 Home + Agent to use any Mobile Nodes. + +MIPv6: Home Agent Support +CONFIG_IPV6_MOBILITY_HA + If you want your router to serve as a MIPv6 Home Agent (HA), select + this option. You must configure HA using the userspace tools + available at http://www.mipl.mediapoli.com/download/mipv6-tools/. + + If your computer is not a router, or you are unsure if you need + this, say N. + +MIPv6: Debug messages +CONFIG_IPV6_MOBILITY_DEBUG + MIPL Mobile IPv6 can produce a lot of debugging messages. There are + eight debug levels (0 through 7) and the level is controlled via + /proc/sys/net/ipv6/mobility/debuglevel. Since MIPL is still + experimental, you might want to say Y here. + + Be sure to say Y and record debug messages when submitting a bug + report. The SCTP Protocol (EXPERIMENTAL) CONFIG_IP_SCTP Stream Control Transmission Protocol diff -uprN linux-2.4.25.old/Documentation/DocBook/Makefile linux-2.4.25/Documentation/DocBook/Makefile --- linux-2.4.25.old/Documentation/DocBook/Makefile 2002-11-28 23:53:08.000000000 +0000 +++ linux-2.4.25/Documentation/DocBook/Makefile 2004-06-26 11:29:29.000000000 +0100 @@ -2,7 +2,7 @@ BOOKS := wanbook.sgml z8530book.sgml mca kernel-api.sgml parportbook.sgml kernel-hacking.sgml \ kernel-locking.sgml via-audio.sgml mousedrivers.sgml sis900.sgml \ deviceiobook.sgml procfs-guide.sgml tulip-user.sgml \ - journal-api.sgml + journal-api.sgml mip6-func.sgml PS := $(patsubst %.sgml, %.ps, $(BOOKS)) PDF := $(patsubst %.sgml, %.pdf, $(BOOKS)) @@ -86,6 +86,9 @@ videobook.sgml: videobook.tmpl $(TOPDIR) procfs-guide.sgml: procfs-guide.tmpl procfs_example.sgml $(TOPDIR)/scripts/docgen < procfs-guide.tmpl >$@ +mip6-func.sgml: mip6-func.tmpl + $(TOPDIR)/scripts/docgen <$< >$@ + APISOURCES := $(TOPDIR)/drivers/media/video/videodev.c \ $(TOPDIR)/arch/i386/kernel/irq.c \ $(TOPDIR)/arch/i386/kernel/mca.c \ diff -uprN linux-2.4.25.old/Documentation/DocBook/mip6-func.tmpl linux-2.4.25/Documentation/DocBook/mip6-func.tmpl --- linux-2.4.25.old/Documentation/DocBook/mip6-func.tmpl 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.25/Documentation/DocBook/mip6-func.tmpl 2004-06-26 11:29:29.000000000 +0100 @@ -0,0 +1,756 @@ + + + + MIPL Mobile IPv6 Function Reference Guide + + + + MIPL Mobile IPv6 for Linux Team + + Helsinki University of Technology + Telecommunications Software and Multimedia Lab +
+ PO BOX 9201 + FIN-02015 HUT + Finland + mipl@list.mipl.mediapoli.com +
+
+
+
+ + + 2000-2001 + Helsinki University of Technology + + + + + Copyright (c) 2001, 2002 MIPL Mobile IPv6 for Linux Team. + + + Permission is granted to copy, distribute and/or modify this + document under the terms of the GNU Free Documentation License, + Version 1.1 published by the Free Software Foundation; with the + Invariant Sections being "Introduction", with the Front-Cover + Texts being "MIPL Mobile IPv6 Function Reference Guide", "MIPL + Mobile IPv6 for Linux Team" and "Helsinki University of + Technology". A copy of the license is included in . + + + +
+ + + + + Introduction + + + MIPL Mobile IPv6 for Linux is an implementation of Mobility + Support in IPv6 IETF mobile-ip working groups Internet-Draft + (draft-ietf-mobileip-ipv6). This implementation has been + developed in the Telecommunications Software and Multimedia + Laboratory at Helsinki University of Technology. + + + + MIPL is fully open source, licensed under the GNU General + Public License. Latest source for MIPL can be downloaded from + the MIPL website at: + + + http://www.mipl.mediapoli.com/. + + + Developers and users interested in MIPL can subscribe to the + MIPL mailing list by sending e-mail to + majordomo@list.mipl.mediapoli.com with + + + subscribe mipl + + + in the body of the message. + + + + This document is a reference guide to MIPL functions. Intended + audience is developers wishing to contribute to the project. + Hopefully this document will make it easier and quicker to + understand and adopt the inner workings of MIPL Mobile IPv6. + + + + MIPL Mobile IPv6 for Linux Team members (past and present): + + + +
+ Sami Kivisaari Sami.Kivisaari@hut.fi +
+
+ +
+ Niklas Kampe Niklas.Kampe@hut.fi +
+
+ +
+ Juha Mynttinen Juha.Mynttinen@hut.fi +
+
+ +
+ Toni Nykanen Toni.Nykanen@iki.fi +
+
+ +
+ Henrik Petander Henrik.Petander@hut.fi +
+
+ +
+ Antti Tuominen ajtuomin@tml.hut.fi +
+
+
+ + + +
+ Marko Myllynen +
+
+ +
+ Ville Nuorvala vnuorval@tcs.hut.fi +
+
+ +
+ Jaakko Laine Jaakko.Laine@hut.fi +
+
+
+
+ +
+ + + Common functions for all entities + + Low-level functions + + These functions implement memory allocation used by others. + Hashlist functions implement a linked list with hash lookup, + which is used with Binding Update List, Binding Cache, Home + Agents List etc. + +!Inet/ipv6/mobile_ip6/mempool.h +!Inet/ipv6/mobile_ip6/hashlist.h + + + Debug functions + + Debug and utility functions. These functions are available if + CONFIG_IPV6_MOBILITY_DEBUG is set. + Otherwise macros expand to no operation. + +!Inet/ipv6/mobile_ip6/debug.h +!Inet/ipv6/mobile_ip6/mipv6.c + + + Extension Header functions + + These functions create and handle extension headers that are + specific to MIPv6. + +!Inet/ipv6/mobile_ip6/exthdrs.c + + + Mobility Header functions + + MIPv6 specifies a new protocol called Mobility Header. + Mobility Header has several message types. Messages may also + carry Mobility Options. These functions are used to create and + handle Mobility Headers and Mobility Options. + +!Inet/ipv6/mobile_ip6/sendopts.c +!Inet/ipv6/mobile_ip6/mh_recv.c +!Inet/ipv6/mobile_ip6/auth_subopt.c + + + Binding Cache + + All Mobile IPv6 entities have a binding cache. These functions + provide easy manipulation of the binding cache. + +!Inet/ipv6/mobile_ip6/bcache.c + + + Security + + + These functions are common authentication functions and + implement Draft 13 style IPSec AH support for Binding Updates. + +!Inet/ipv6/mobile_ip6/ah_algo.c +!Inet/ipv6/mobile_ip6/sadb.c +!Inet/ipv6/mobile_ip6/ah.c + + + Utility functions + + + These functions are general utility functions commonly used by + all entities. + +!Inet/ipv6/mobile_ip6/util.c + + + + + + Mobile Node functions + General functions + + +!Inet/ipv6/mobile_ip6/mn.c + + + Binding Update List + + Mobile Node keeps track of sent binding updates in Binding + Update List. + +!Inet/ipv6/mobile_ip6/bul.c + + + Movement detection + + + These functions are used by the mobile node for movement + detection. + +!Inet/ipv6/mobile_ip6/mdetect.c + + + + + Home Agent functions + General functions + + +!Inet/ipv6/mobile_ip6/ha.c + + + Duplicate Address Detection functions + + Home Agent does Duplicate Address Detection for Mobile Nodes' + addresses. These functions implement MIPv6 specific DAD + functionality. + +!Inet/ipv6/mobile_ip6/dad.c + + + + + GNU Free Documentation License + + + Version 1.1, March 2000 + + + + Copyright (C) 2000 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + 0. PREAMBLE + + + The purpose of this License is to make a manual, textbook, or + other written document "free" in the sense of freedom: to + assure everyone the effective freedom to copy and redistribute + it, with or without modifying it, either commercially or + noncommercially. Secondarily, this License preserves for the + author and publisher a way to get credit for their work, while + not being considered responsible for modifications made by + others. + + + + This License is a kind of "copyleft", which means that + derivative works of the document must themselves be free in the + same sense. It complements the GNU General Public License, + which is a copyleft license designed for free software. + + + + We have designed this License in order to use it for manuals + for free software, because free software needs free + documentation: a free program should come with manuals + providing the same freedoms that the software does. But this + License is not limited to software manuals; it can be used for + any textual work, regardless of subject matter or whether it is + published as a printed book. We recommend this License + principally for works whose purpose is instruction or + reference. + + + + 1. APPLICABILITY AND DEFINITIONS + + + This License applies to any manual or other work that contains + a notice placed by the copyright holder saying it can be + distributed under the terms of this License. The "Document", + below, refers to any such manual or work. Any member of the + public is a licensee, and is addressed as "you". + + + + A "Modified Version" of the Document means any work containing + the Document or a portion of it, either copied verbatim, or + with modifications and/or translated into another language. + + + + A "Secondary Section" is a named appendix or a front-matter + section of the Document that deals exclusively with the + relationship of the publishers or authors of the Document to + the Document's overall subject (or to related matters) and + contains nothing that could fall directly within that overall + subject. (For example, if the Document is in part a textbook of + mathematics, a Secondary Section may not explain any + mathematics.) The relationship could be a matter of historical + connection with the subject or with related matters, or of + legal, commercial, philosophical, ethical or political position + regarding them. + + + + The "Invariant Sections" are certain Secondary Sections whose + titles are designated, as being those of Invariant Sections, in + the notice that says that the Document is released under this + License. + + + + The "Cover Texts" are certain short passages of text that are + listed, as Front-Cover Texts or Back-Cover Texts, in the notice + that says that the Document is released under this License. + + + + A "Transparent" copy of the Document means a machine-readable + copy, represented in a format whose specification is available + to the general public, whose contents can be viewed and edited + directly and straightforwardly with generic text editors or + (for images composed of pixels) generic paint programs or (for + drawings) some widely available drawing editor, and that is + suitable for input to text formatters or for automatic + translation to a variety of formats suitable for input to text + formatters. A copy made in an otherwise Transparent file format + whose markup has been designed to thwart or discourage + subsequent modification by readers is not Transparent. A copy + that is not "Transparent" is called "Opaque". + + + + Examples of suitable formats for Transparent copies include + plain ASCII without markup, Texinfo input format, LaTeX input + format, SGML or XML using a publicly available DTD, and + standard-conforming simple HTML designed for human + modification. Opaque formats include PostScript, PDF, + proprietary formats that can be read and edited only by + proprietary word processors, SGML or XML for which the DTD + and/or processing tools are not generally available, and the + machine-generated HTML produced by some word processors for + output purposes only. + + + + The "Title Page" means, for a printed book, the title page + itself, plus such following pages as are needed to hold, + legibly, the material this License requires to appear in the + title page. For works in formats which do not have any title + page as such, "Title Page" means the text near the most + prominent appearance of the work's title, preceding the + beginning of the body of the text. + + + + 2. VERBATIM COPYING + + + You may copy and distribute the Document in any medium, either + commercially or noncommercially, provided that this License, + the copyright notices, and the license notice saying this + License applies to the Document are reproduced in all copies, + and that you add no other conditions whatsoever to those of + this License. You may not use technical measures to obstruct or + control the reading or further copying of the copies you make + or distribute. However, you may accept compensation in exchange + for copies. If you distribute a large enough number of copies + you must also follow the conditions in section 3. + + + + You may also lend copies, under the same conditions stated + above, and you may publicly display copies. + + + + 3. COPYING IN QUANTITY + + + If you publish printed copies of the Document numbering more + than 100, and the Document's license notice requires Cover + Texts, you must enclose the copies in covers that carry, + clearly and legibly, all these Cover Texts: Front-Cover Texts + on the front cover, and Back-Cover Texts on the back + cover. Both covers must also clearly and legibly identify you + as the publisher of these copies. The front cover must present + the full title with all words of the title equally prominent + and visible. You may add other material on the covers in + addition. Copying with changes limited to the covers, as long + as they preserve the title of the Document and satisfy these + conditions, can be treated as verbatim copying in other + respects. + + + + If the required texts for either cover are too voluminous to + fit legibly, you should put the first ones listed (as many as + fit reasonably) on the actual cover, and continue the rest onto + adjacent pages. + + + + If you publish or distribute Opaque copies of the Document + numbering more than 100, you must either include a + machine-readable Transparent copy along with each Opaque copy, + or state in or with each Opaque copy a publicly-accessible + computer-network location containing a complete Transparent + copy of the Document, free of added material, which the general + network-using public has access to download anonymously at no + charge using public-standard network protocols. If you use the + latter option, you must take reasonably prudent steps, when you + begin distribution of Opaque copies in quantity, to ensure that + this Transparent copy will remain thus accessible at the stated + location until at least one year after the last time you + distribute an Opaque copy (directly or through your agents or + retailers) of that edition to the public. + + + + It is requested, but not required, that you contact the authors + of the Document well before redistributing any large number of + copies, to give them a chance to provide you with an updated + version of the Document. + + + + 4. MODIFICATIONS + + + You may copy and distribute a Modified Version of the Document + under the conditions of sections 2 and 3 above, provided that + you release the Modified Version under precisely this License, + with the Modified Version filling the role of the Document, + thus licensing distribution and modification of the Modified + Version to whoever possesses a copy of it. In addition, you + must do these things in the Modified Version: + + + + + + + A. Use in the Title Page (and on the covers, if any) a title + distinct from that of the Document, and from those of previous + versions (which should, if there were any, be listed in the + History section of the Document). You may use the same title + as a previous version if the original publisher of that + version gives permission. + + + + + B. List on the Title Page, as authors, one or more persons + or entities responsible for authorship of the modifications in + the Modified Version, together with at least five of the + principal authors of the Document (all of its principal + authors, if it has less than five). + + + + + C. State on the Title page the name of the publisher of the + Modified Version, as the publisher. + + + + + D. Preserve all the copyright notices of the Document. + + + + + E. Add an appropriate copyright notice for your + modifications adjacent to the other copyright notices. + + + + + F. Include, immediately after the copyright notices, a + license notice giving the public permission to use the + Modified Version under the terms of this License, in the form + shown in the Addendum below. + + + + + G. Preserve in that license notice the full lists of + Invariant Sections and required Cover Texts given in the + Document's license notice. + + + + + H. Include an unaltered copy of this License. + + + + + I. Preserve the section entitled "History", and its title, + and add to it an item stating at least the title, year, new + authors, and publisher of the Modified Version as given on the + Title Page. If there is no section entitled "History" in the + Document, create one stating the title, year, authors, and + publisher of the Document as given on its Title Page, then add + an item describing the Modified Version as stated in the + previous sentence. + + + + + J. Preserve the network location, if any, given in the + Document for public access to a Transparent copy of the + Document, and likewise the network locations given in the + Document for previous versions it was based on. These may be + placed in the "History" section. You may omit a network + location for a work that was published at least four years + before the Document itself, or if the original publisher of + the version it refers to gives permission. + + + + + K. In any section entitled "Acknowledgements" or + "Dedications", preserve the section's title, and preserve in + the section all the substance and tone of each of the + contributor acknowledgements and/or dedications given therein. + + + + + L. Preserve all the Invariant Sections of the Document, + unaltered in their text and in their titles. Section numbers + or the equivalent are not considered part of the section + titles. + + + + + M. Delete any section entitled "Endorsements". Such a + section may not be included in the Modified Version. + + + + + N. Do not retitle any existing section as "Endorsements" or + to conflict in title with any Invariant Section. + + + + + + + If the Modified Version includes new front-matter sections or + appendices that qualify as Secondary Sections and contain no + material copied from the Document, you may at your option + designate some or all of these sections as invariant. To do + this, add their titles to the list of Invariant Sections in the + Modified Version's license notice. These titles must be + distinct from any other section titles. + + + + You may add a section entitled "Endorsements", provided it + contains nothing but endorsements of your Modified Version by + various parties--for example, statements of peer review or that + the text has been approved by an organization as the + authoritative definition of a standard. + + + + You may add a passage of up to five words as a Front-Cover + Text, and a passage of up to 25 words as a Back-Cover Text, to + the end of the list of Cover Texts in the Modified + Version. Only one passage of Front-Cover Text and one of + Back-Cover Text may be added by (or through arrangements made + by) any one entity. If the Document already includes a cover + text for the same cover, previously added by you or by + arrangement made by the same entity you are acting on behalf + of, you may not add another; but you may replace the old one, + on explicit permission from the previous publisher that added + the old one. + + + + The author(s) and publisher(s) of the Document do not by this + License give permission to use their names for publicity for or + to assert or imply endorsement of any Modified Version. + + + + 5. COMBINING DOCUMENTS + + + You may combine the Document with other documents released + under this License, under the terms defined in section 4 above + for modified versions, provided that you include in the + combination all of the Invariant Sections of all of the + original documents, unmodified, and list them all as Invariant + Sections of your combined work in its license notice. + + + + The combined work need only contain one copy of this License, + and multiple identical Invariant Sections may be replaced with + a single copy. If there are multiple Invariant Sections with + the same name but different contents, make the title of each + such section unique by adding at the end of it, in parentheses, + the name of the original author or publisher of that section if + known, or else a unique number. Make the same adjustment to the + section titles in the list of Invariant Sections in the license + notice of the combined work. + + + + In the combination, you must combine any sections entitled + "History" in the various original documents, forming one + section entitled "History"; likewise combine any sections + entitled "Acknowledgements", and any sections entitled + "Dedications". You must delete all sections entitled + "Endorsements." + + + + 6. COLLECTIONS OF DOCUMENTS + + + You may make a collection consisting of the Document and other + documents released under this License, and replace the + individual copies of this License in the various documents with + a single copy that is included in the collection, provided that + you follow the rules of this License for verbatim copying of + each of the documents in all other respects. + + + + You may extract a single document from such a collection, and + distribute it individually under this License, provided you + insert a copy of this License into the extracted document, and + follow this License in all other respects regarding verbatim + copying of that document. + + + + 7. AGGREGATION WITH INDEPENDENT WORKS + + + A compilation of the Document or its derivatives with other + separate and independent documents or works, in or on a volume + of a storage or distribution medium, does not as a whole count + as a Modified Version of the Document, provided no compilation + copyright is claimed for the compilation. Such a compilation is + called an "aggregate", and this License does not apply to the + other self-contained works thus compiled with the Document, on + account of their being thus compiled, if they are not + themselves derivative works of the Document. + + + + If the Cover Text requirement of section 3 is applicable to + these copies of the Document, then if the Document is less than + one quarter of the entire aggregate, the Document's Cover Texts + may be placed on covers that surround only the Document within + the aggregate. Otherwise they must appear on covers around the + whole aggregate. + + + + 8. TRANSLATION + + + Translation is considered a kind of modification, so you may + distribute translations of the Document under the terms of + section 4. Replacing Invariant Sections with translations + requires special permission from their copyright holders, but + you may include translations of some or all Invariant Sections + in addition to the original versions of these Invariant + Sections. You may include a translation of this License + provided that you also include the original English version of + this License. In case of a disagreement between the translation + and the original English version of this License, the original + English version will prevail. + + + + 9. TERMINATION + + + You may not copy, modify, sublicense, or distribute the + Document except as expressly provided for under this + License. Any other attempt to copy, modify, sublicense or + distribute the Document is void, and will automatically + terminate your rights under this License. However, parties who + have received copies, or rights, from you under this License + will not have their licenses terminated so long as such parties + remain in full compliance. + + + + 10. FUTURE REVISIONS OF THIS LICENSE + + + The Free Software Foundation may publish new, revised versions + of the GNU Free Documentation License from time to time. Such + new versions will be similar in spirit to the present version, + but may differ in detail to address new problems or + concerns. See http://www.gnu.org/copyleft/. + + + + Each version of the License is given a distinguishing version + number. If the Document specifies that a particular numbered + version of this License "or any later version" applies to it, + you have the option of following the terms and conditions + either of that specified version or of any later version that + has been published (not as a draft) by the Free Software + Foundation. If the Document does not specify a version number + of this License, you may choose any version ever published (not + as a draft) by the Free Software Foundation. + + + + +
diff -uprN linux-2.4.25.old/include/linux/icmpv6.h linux-2.4.25/include/linux/icmpv6.h --- linux-2.4.25.old/include/linux/icmpv6.h 2003-08-25 12:44:44.000000000 +0100 +++ linux-2.4.25/include/linux/icmpv6.h 2004-06-26 11:29:29.000000000 +0100 @@ -40,14 +40,16 @@ struct icmp6hdr { struct icmpv6_nd_ra { __u8 hop_limit; #if defined(__LITTLE_ENDIAN_BITFIELD) - __u8 reserved:6, + __u8 reserved:5, + home_agent:1, other:1, managed:1; #elif defined(__BIG_ENDIAN_BITFIELD) __u8 managed:1, other:1, - reserved:6; + home_agent:1, + reserved:5; #else #error "Please fix " #endif @@ -70,6 +72,7 @@ struct icmp6hdr { #define icmp6_addrconf_managed icmp6_dataun.u_nd_ra.managed #define icmp6_addrconf_other icmp6_dataun.u_nd_ra.other #define icmp6_rt_lifetime icmp6_dataun.u_nd_ra.rt_lifetime +#define icmp6_home_agent icmp6_dataun.u_nd_ra.home_agent }; diff -uprN linux-2.4.25.old/include/linux/if_arp.h linux-2.4.25/include/linux/if_arp.h --- linux-2.4.25.old/include/linux/if_arp.h 2002-02-25 19:38:13.000000000 +0000 +++ linux-2.4.25/include/linux/if_arp.h 2004-06-26 11:29:29.000000000 +0100 @@ -59,7 +59,7 @@ #define ARPHRD_RAWHDLC 518 /* Raw HDLC */ #define ARPHRD_TUNNEL 768 /* IPIP tunnel */ -#define ARPHRD_TUNNEL6 769 /* IPIP6 tunnel */ +#define ARPHRD_TUNNEL6 769 /* IP6IP6 tunnel */ #define ARPHRD_FRAD 770 /* Frame Relay Access Device */ #define ARPHRD_SKIP 771 /* SKIP vif */ #define ARPHRD_LOOPBACK 772 /* Loopback device */ diff -uprN linux-2.4.25.old/include/linux/in6.h linux-2.4.25/include/linux/in6.h --- linux-2.4.25.old/include/linux/in6.h 2003-06-13 15:51:38.000000000 +0100 +++ linux-2.4.25/include/linux/in6.h 2004-06-26 11:29:29.000000000 +0100 @@ -142,6 +142,11 @@ struct in6_flowlabel_req #define IPV6_TLV_JUMBO 194 /* + * Mobile IPv6 TLV options. + */ +#define MIPV6_TLV_HOMEADDR 201 + +/* * IPV6 socket options */ diff -uprN linux-2.4.25.old/include/linux/ipv6.h linux-2.4.25/include/linux/ipv6.h --- linux-2.4.25.old/include/linux/ipv6.h 2003-11-28 18:26:21.000000000 +0000 +++ linux-2.4.25/include/linux/ipv6.h 2004-06-26 11:29:29.000000000 +0100 @@ -29,6 +29,7 @@ struct in6_ifreq { #define IPV6_SRCRT_STRICT 0x01 /* this hop must be a neighbor */ #define IPV6_SRCRT_TYPE_0 0 /* IPv6 type 0 Routing Header */ +#define IPV6_SRCRT_TYPE_2 2 /* type 2 for Mobile IPv6 */ /* * routing header @@ -71,6 +72,19 @@ struct rt0_hdr { struct in6_addr addr[0]; #define rt0_type rt_hdr.type + +}; + +/* + * routing header type 2 + */ + +struct rt2_hdr { + struct ipv6_rt_hdr rt_hdr; + __u32 reserved; + struct in6_addr addr; + +#define rt2_type rt_hdr.type; }; /* @@ -156,12 +170,16 @@ enum { struct inet6_skb_parm { int iif; + __u8 mipv6_flags; __u16 ra; __u16 hop; __u16 auth; __u16 dst0; __u16 srcrt; + __u16 srcrt2; + __u16 hao; __u16 dst1; + struct in6_addr hoa; }; #endif diff -uprN linux-2.4.25.old/include/linux/ipv6_route.h linux-2.4.25/include/linux/ipv6_route.h --- linux-2.4.25.old/include/linux/ipv6_route.h 2003-11-28 18:26:21.000000000 +0000 +++ linux-2.4.25/include/linux/ipv6_route.h 2004-06-26 11:29:29.000000000 +0100 @@ -33,6 +33,7 @@ enum #define RTF_CACHE 0x01000000 /* cache entry */ #define RTF_FLOW 0x02000000 /* flow significant route */ #define RTF_POLICY 0x04000000 /* policy route */ +#define RTF_MOBILENODE 0x10000000 /* for routing to Mobile Node */ #define RTF_LOCAL 0x80000000 diff -uprN linux-2.4.25.old/include/linux/ipv6_tunnel.h linux-2.4.25/include/linux/ipv6_tunnel.h --- linux-2.4.25.old/include/linux/ipv6_tunnel.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.25/include/linux/ipv6_tunnel.h 2004-06-26 11:29:29.000000000 +0100 @@ -0,0 +1,34 @@ +/* + * $Id$ + */ + +#ifndef _IPV6_TUNNEL_H +#define _IPV6_TUNNEL_H + +#define IPV6_TLV_TNL_ENCAP_LIMIT 4 +#define IPV6_DEFAULT_TNL_ENCAP_LIMIT 4 + +/* don't add encapsulation limit if one isn't present in inner packet */ +#define IP6_TNL_F_IGN_ENCAP_LIMIT 0x1 +/* copy the traffic class field from the inner packet */ +#define IP6_TNL_F_USE_ORIG_TCLASS 0x2 +/* copy the flowlabel from the inner packet */ +#define IP6_TNL_F_USE_ORIG_FLOWLABEL 0x4 +/* created and maintained from within the kernel */ +#define IP6_TNL_F_KERNEL_DEV 0x8 +/* being used for Mobile IPv6 */ +#define IP6_TNL_F_MIP6_DEV 0x10 + +struct ip6_tnl_parm { + char name[IFNAMSIZ]; /* name of tunnel device */ + int link; /* ifindex of underlying L2 interface */ + __u8 proto; /* tunnel protocol */ + __u8 encap_limit; /* encapsulation limit for tunnel */ + __u8 hop_limit; /* hop limit for tunnel */ + __u32 flowinfo; /* traffic class and flowlabel for tunnel */ + __u32 flags; /* tunnel flags */ + struct in6_addr laddr; /* local tunnel end-point address */ + struct in6_addr raddr; /* remote tunnel end-point address */ +}; + +#endif diff -uprN linux-2.4.25.old/include/linux/rtnetlink.h linux-2.4.25/include/linux/rtnetlink.h --- linux-2.4.25.old/include/linux/rtnetlink.h 2004-02-18 13:36:32.000000000 +0000 +++ linux-2.4.25/include/linux/rtnetlink.h 2004-06-26 11:29:29.000000000 +0100 @@ -309,15 +309,17 @@ enum IFA_LABEL, IFA_BROADCAST, IFA_ANYCAST, - IFA_CACHEINFO + IFA_CACHEINFO, + IFA_HOMEAGENT }; -#define IFA_MAX IFA_CACHEINFO +#define IFA_MAX IFA_HOMEAGENT /* ifa_flags */ #define IFA_F_SECONDARY 0x01 +#define IFA_F_HOMEADDR 0x10 #define IFA_F_DEPRECATED 0x20 #define IFA_F_TENTATIVE 0x40 #define IFA_F_PERMANENT 0x80 diff -uprN linux-2.4.25.old/include/linux/skbuff.h linux-2.4.25/include/linux/skbuff.h --- linux-2.4.25.old/include/linux/skbuff.h 2003-08-25 12:44:44.000000000 +0100 +++ linux-2.4.25/include/linux/skbuff.h 2004-06-26 11:29:29.000000000 +0100 @@ -177,7 +177,7 @@ struct sk_buff { * want to keep them across layers you have to do a skb_clone() * first. This is owned by whoever has the skb queued ATM. */ - char cb[48]; + char cb[64]; unsigned int len; /* Length of actual data */ unsigned int data_len; diff -uprN linux-2.4.25.old/include/linux/sysctl.h linux-2.4.25/include/linux/sysctl.h --- linux-2.4.25.old/include/linux/sysctl.h 2004-02-18 13:36:32.000000000 +0000 +++ linux-2.4.25/include/linux/sysctl.h 2004-06-26 11:29:29.000000000 +0100 @@ -387,7 +387,24 @@ enum { NET_IPV6_NEIGH=17, NET_IPV6_ROUTE=18, NET_IPV6_ICMP=19, - NET_IPV6_BINDV6ONLY=20 + NET_IPV6_BINDV6ONLY=20, + NET_IPV6_MOBILITY=26 +}; + +/* /proc/sys/net/ipv6/mobility */ +enum { + NET_IPV6_MOBILITY_DEBUG=1, + NET_IPV6_MOBILITY_TUNNEL_SITELOCAL=2, + NET_IPV6_MOBILITY_ROUTER_SOLICITATION_MAX_SENDTIME=3, + NET_IPV6_MOBILITY_ROUTER_REACH=4, + NET_IPV6_MOBILITY_MDETECT_MECHANISM=5, + NET_IPV6_MOBILITY_RETROUT=6, + NET_IPV6_MOBILITY_MAX_TNLS=7, + NET_IPV6_MOBILITY_MIN_TNLS=8, + NET_IPV6_MOBILITY_BINDING_REFRESH=9, + NET_IPV6_MOBILITY_BU_F_LLADDR=10, + NET_IPV6_MOBILITY_BU_F_KEYMGM=11, + NET_IPV6_MOBILITY_BU_F_CN_ACK=12 }; enum { diff -uprN linux-2.4.25.old/include/net/addrconf.h linux-2.4.25/include/net/addrconf.h --- linux-2.4.25.old/include/net/addrconf.h 2003-08-25 12:44:44.000000000 +0100 +++ linux-2.4.25/include/net/addrconf.h 2004-06-26 11:29:29.000000000 +0100 @@ -16,9 +16,11 @@ struct prefix_info { #if defined(__BIG_ENDIAN_BITFIELD) __u8 onlink : 1, autoconf : 1, - reserved : 6; + router_address : 1, + reserved : 5; #elif defined(__LITTLE_ENDIAN_BITFIELD) - __u8 reserved : 6, + __u8 reserved : 5, + router_address : 1, autoconf : 1, onlink : 1; #else @@ -55,6 +57,7 @@ extern int ipv6_chk_addr(struct in6_ad struct net_device *dev); extern struct inet6_ifaddr * ipv6_get_ifaddr(struct in6_addr *addr, struct net_device *dev); +extern void ipv6_del_addr(struct inet6_ifaddr *ifp); extern int ipv6_get_saddr(struct dst_entry *dst, struct in6_addr *daddr, struct in6_addr *saddr); @@ -85,7 +88,9 @@ extern void ipv6_mc_up(struct inet6_dev extern void ipv6_mc_down(struct inet6_dev *idev); extern void ipv6_mc_init_dev(struct inet6_dev *idev); extern void ipv6_mc_destroy_dev(struct inet6_dev *idev); +extern void addrconf_dad_start(struct inet6_ifaddr *ifp, int flags); extern void addrconf_dad_failure(struct inet6_ifaddr *ifp); +extern void addrconf_dad_completed(struct inet6_ifaddr *ifp); extern int ipv6_chk_mcast_addr(struct net_device *dev, struct in6_addr *group, struct in6_addr *src_addr); @@ -116,6 +121,9 @@ extern int ipv6_chk_acast_addr(struct extern int register_inet6addr_notifier(struct notifier_block *nb); extern int unregister_inet6addr_notifier(struct notifier_block *nb); +extern int ipv6_generate_eui64(u8 *eui, struct net_device *dev); +extern int ipv6_inherit_eui64(u8 *eui, struct inet6_dev *idev); + static inline struct inet6_dev * __in6_dev_get(struct net_device *dev) { diff -uprN linux-2.4.25.old/include/net/ip6_route.h linux-2.4.25/include/net/ip6_route.h --- linux-2.4.25.old/include/net/ip6_route.h 2003-06-13 15:51:39.000000000 +0100 +++ linux-2.4.25/include/net/ip6_route.h 2004-06-26 11:29:29.000000000 +0100 @@ -2,6 +2,7 @@ #define _NET_IP6_ROUTE_H #define IP6_RT_PRIO_FW 16 +#define IP6_RT_PRIO_MIPV6 64 #define IP6_RT_PRIO_USER 1024 #define IP6_RT_PRIO_ADDRCONF 256 #define IP6_RT_PRIO_KERN 512 @@ -40,6 +41,9 @@ extern int ipv6_route_ioctl(unsigned i extern int ip6_route_add(struct in6_rtmsg *rtmsg, struct nlmsghdr *); + +extern int ip6_route_del(struct in6_rtmsg *rtmsg, + struct nlmsghdr *); extern int ip6_del_rt(struct rt6_info *, struct nlmsghdr *); @@ -99,7 +103,8 @@ extern rwlock_t rt6_lock; */ static inline void ip6_dst_store(struct sock *sk, struct dst_entry *dst, - struct in6_addr *daddr) + struct in6_addr *daddr, + struct in6_addr *saddr) { struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6; struct rt6_info *rt = (struct rt6_info *) dst; @@ -107,6 +112,9 @@ static inline void ip6_dst_store(struct write_lock(&sk->dst_lock); __sk_dst_set(sk, dst); np->daddr_cache = daddr; +#ifdef CONFIG_IPV6_SUBTREES + np->saddr_cache = saddr; +#endif np->dst_cookie = rt->rt6i_node ? rt->rt6i_node->fn_sernum : 0; write_unlock(&sk->dst_lock); } diff -uprN linux-2.4.25.old/include/net/ipv6.h linux-2.4.25/include/net/ipv6.h --- linux-2.4.25.old/include/net/ipv6.h 2003-11-28 18:26:21.000000000 +0000 +++ linux-2.4.25/include/net/ipv6.h 2004-06-26 11:29:29.000000000 +0100 @@ -37,6 +37,7 @@ #define NEXTHDR_ICMP 58 /* ICMP for IPv6. */ #define NEXTHDR_NONE 59 /* No next header */ #define NEXTHDR_DEST 60 /* Destination options header. */ +#define NEXTHDR_MH 135 /* Mobility header, RFC 3775 */ #define NEXTHDR_MAX 255 @@ -145,9 +146,12 @@ struct ipv6_txoptions __u16 opt_flen; /* after fragment hdr */ __u16 opt_nflen; /* before fragment hdr */ + __u8 mipv6_flags; /* flags set by MIPv6 */ + struct ipv6_opt_hdr *hopopt; struct ipv6_opt_hdr *dst0opt; - struct ipv6_rt_hdr *srcrt; /* Routing Header */ + struct ipv6_rt_hdr *srcrt; /* Routing Header Type 0 */ + struct ipv6_rt_hdr *srcrt2; /* Routing Header Type 2 */ struct ipv6_opt_hdr *auth; struct ipv6_opt_hdr *dst1opt; @@ -256,6 +260,38 @@ static inline int ipv6_addr_any(const st a->s6_addr32[2] | a->s6_addr32[3] ) == 0); } +static inline void ipv6_addr_prefix(struct in6_addr *pfx, + const struct in6_addr *addr, int plen) +{ + /* caller must guarantee 0 <= plen <= 128 */ + int o = plen >> 3, + b = plen & 0x7; + + memcpy(pfx->s6_addr, addr, o); + if (b != 0) { + pfx->s6_addr[o] = addr->s6_addr[o] & (0xff00 >> b); + o++; + } + if (o < 16) + memset(pfx->s6_addr + o, 0, 16 - o); +} + +static inline int ipv6_prefix_cmp(const struct in6_addr *p1, + const struct in6_addr *p2, int plen) +{ + int b = plen&0x7; + int o = plen>>3; + int res = 0; + + if (o > 0) + res = memcmp(&p1->s6_addr[0], &p2->s6_addr[0], o); + if (res == 0 && b > 0) { + __u8 m = (0xff00 >> b) & 0xff; + res = (p1->s6_addr[o] & m) - (p2->s6_addr[o] & m); + } + return res; +} + /* * Prototypes exported by ipv6 */ diff -uprN linux-2.4.25.old/include/net/ipv6_tunnel.h linux-2.4.25/include/net/ipv6_tunnel.h --- linux-2.4.25.old/include/net/ipv6_tunnel.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.25/include/net/ipv6_tunnel.h 2004-06-26 11:29:29.000000000 +0100 @@ -0,0 +1,92 @@ +/* + * $Id$ + */ + +#ifndef _NET_IPV6_TUNNEL_H +#define _NET_IPV6_TUNNEL_H + +#include +#include +#include +#include +#include + +/* capable of sending packets */ +#define IP6_TNL_F_CAP_XMIT 0x10000 +/* capable of receiving packets */ +#define IP6_TNL_F_CAP_RCV 0x20000 + +#define IP6_TNL_MAX 128 + +/* IPv6 tunnel */ + +struct ip6_tnl { + struct ip6_tnl *next; /* next tunnel in list */ + struct net_device *dev; /* virtual device associated with tunnel */ + struct net_device_stats stat; /* statistics for tunnel device */ + int recursion; /* depth of hard_start_xmit recursion */ + struct ip6_tnl_parm parms; /* tunnel configuration paramters */ + struct flowi fl; /* flowi template for xmit */ + atomic_t refcnt; /* nr of identical tunnels used by kernel */ + struct socket *sock; +}; + +#define IP6_TNL_PRE_ENCAP 0 +#define IP6_TNL_PRE_DECAP 1 +#define IP6_TNL_MAXHOOKS 2 + +#define IP6_TNL_DROP 0 +#define IP6_TNL_ACCEPT 1 + +typedef int ip6_tnl_hookfn(struct ip6_tnl *t, struct sk_buff *skb); + +struct ip6_tnl_hook_ops { + struct list_head list; + unsigned int hooknum; + int priority; + ip6_tnl_hookfn *hook; +}; + +enum ip6_tnl_hook_priorities { + IP6_TNL_PRI_FIRST = INT_MIN, + IP6_TNL_PRI_LAST = INT_MAX +}; + +/* Tunnel encapsulation limit destination sub-option */ + +struct ipv6_tlv_tnl_enc_lim { + __u8 type; /* type-code for option */ + __u8 length; /* option length */ + __u8 encap_limit; /* tunnel encapsulation limit */ +} __attribute__ ((packed)); + +#ifdef __KERNEL__ +extern int ip6ip6_tnl_create(struct ip6_tnl_parm *p, struct ip6_tnl **pt); + +extern struct ip6_tnl *ip6ip6_tnl_lookup(struct in6_addr *remote, + struct in6_addr *local); + +void ip6ip6_tnl_change(struct ip6_tnl *t, struct ip6_tnl_parm *p); + +extern int ip6ip6_kernel_tnl_add(struct ip6_tnl_parm *p); + +extern int ip6ip6_kernel_tnl_del(struct ip6_tnl *t); + +extern unsigned int ip6ip6_tnl_inc_max_kdev_count(unsigned int n); + +extern unsigned int ip6ip6_tnl_dec_max_kdev_count(unsigned int n); + +extern unsigned int ip6ip6_tnl_inc_min_kdev_count(unsigned int n); + +extern unsigned int ip6ip6_tnl_dec_min_kdev_count(unsigned int n); + +extern void ip6ip6_tnl_register_hook(struct ip6_tnl_hook_ops *reg); + +extern void ip6ip6_tnl_unregister_hook(struct ip6_tnl_hook_ops *reg); + +#ifdef CONFIG_IPV6_TUNNEL +extern int __init ip6_tunnel_init(void); +extern void ip6_tunnel_cleanup(void); +#endif +#endif +#endif diff -uprN linux-2.4.25.old/include/net/mipglue.h linux-2.4.25/include/net/mipglue.h --- linux-2.4.25.old/include/net/mipglue.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.25/include/net/mipglue.h 2004-06-26 11:29:29.000000000 +0100 @@ -0,0 +1,266 @@ +/* + * Glue for Mobility support integration to IPv6 + * + * Authors: + * Antti Tuominen + * + * $Id$ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + */ + +#ifndef _NET_MIPGLUE_H +#define _NET_MIPGLUE_H + +#ifndef USE_IPV6_MOBILITY +#if defined(CONFIG_IPV6_MOBILITY) || defined(CONFIG_IPV6_MOBILITY_MODULE) +#define USE_IPV6_MOBILITY +#endif +#endif + +/* symbols to indicate whether destination options received should take + * effect or not (see exthdrs.c, procrcv.c) + */ +#define MIPV6_DSTOPTS_ACCEPT 1 +#define MIPV6_DSTOPTS_DISCARD 0 + +#define MIPV6_IGN_RTR 0 +#define MIPV6_ADD_RTR 1 +#define MIPV6_CHG_RTR 2 + +/* MIPV6: Approximate maximum for mobile IPv6 options and headers */ +#define MIPV6_HEADERS 48 + +#ifdef __KERNEL__ +#include +#include +#include + +struct sk_buff; +struct ndisc_options; +struct sock; +struct ipv6_txoptions; +struct flowi; +struct dst_entry; +struct in6_addr; +struct inet6_ifaddr; + +#ifdef USE_IPV6_MOBILITY + +/* calls a procedure from mipv6-module */ +#define MIPV6_CALLPROC(X) if(mipv6_functions.X) mipv6_functions.X + +/* calls a function from mipv6-module, default-value if function not defined + */ +#define MIPV6_CALLFUNC(X,Y) (!mipv6_functions.X)?(Y):mipv6_functions.X + +/* sets a handler-function to process a call */ +#define MIPV6_SETCALL(X,Y) if(mipv6_functions.X) printk("mipv6: Warning, function assigned twice!\n"); \ + mipv6_functions.X = Y +#define MIPV6_RESETCALL(X) mipv6_functions.X = NULL + +/* pointers to mipv6 callable functions */ +struct mipv6_callable_functions { + void (*mipv6_initialize_dstopt_rcv) (struct sk_buff *skb); + int (*mipv6_finalize_dstopt_rcv) (int process); + int (*mipv6_handle_homeaddr) (struct sk_buff *skb, int optoff); + int (*mipv6_ra_rcv) (struct sk_buff *skb, + struct ndisc_options *ndopts); + void (*mipv6_icmp_rcv) (struct sk_buff *skb); + struct ipv6_txoptions * (*mipv6_modify_txoptions) ( + struct sock *sk, + struct sk_buff *skb, + struct ipv6_txoptions *opt, + struct flowi *fl, + struct dst_entry **dst); + void (*mipv6_set_home) (int ifindex, struct in6_addr *homeaddr, + int plen, struct in6_addr *homeagent, + int plen2); + void (*mipv6_get_home_address) (struct in6_addr *home_addr); + void (*mipv6_get_care_of_address)(struct in6_addr *homeaddr, + struct in6_addr *coa); + int (*mipv6_is_home_addr)(struct in6_addr *addr); + void (*mipv6_change_router)(void); + void (*mipv6_check_dad)(struct in6_addr *home_addr); + void (*mipv6_icmp_swap_addrs)(struct sk_buff *skb); + int (*mipv6_forward)(struct sk_buff *skb); + int (*mipv6_mn_ha_probe)(struct inet6_ifaddr *ifp, u8 *lladdr); +}; + +extern struct mipv6_callable_functions mipv6_functions; + +extern void mipv6_invalidate_calls(void); + +extern int mipv6_handle_dstopt(struct sk_buff *skb, int optoff); + +static inline int +ndisc_mip_mn_ha_probe(struct inet6_ifaddr *ifp, u8 *lladdr) +{ + return MIPV6_CALLFUNC(mipv6_mn_ha_probe, 0)(ifp, lladdr); +} + +/* Must only be called for HA, no checks here */ +static inline int ip6_mipv6_forward(struct sk_buff *skb) +{ + return MIPV6_CALLFUNC(mipv6_forward, 0)(skb); +} + +/* + * Avoid adding new default routers if the old one is still in use + */ + +static inline int ndisc_mipv6_ra_rcv(struct sk_buff *skb, + struct ndisc_options *ndopts) +{ + return MIPV6_CALLFUNC(mipv6_ra_rcv, MIPV6_ADD_RTR)(skb, ndopts); +} + +static inline int ipv6_chk_mip_home_addr(struct in6_addr *addr) +{ + return MIPV6_CALLFUNC(mipv6_is_home_addr, 0)(addr); +} + +static inline void ndisc_mipv6_change_router(int change_rtr) +{ + if (change_rtr == MIPV6_CHG_RTR) + MIPV6_CALLPROC(mipv6_change_router)(); +} + +static inline void ndisc_check_mipv6_dad(struct in6_addr *target) +{ + MIPV6_CALLPROC(mipv6_check_dad)(target); +} + +static inline void icmpv6_swap_mipv6_addrs(struct sk_buff *skb) +{ + MIPV6_CALLPROC(mipv6_icmp_swap_addrs)(skb); +} + +static inline void mipv6_icmp_rcv(struct sk_buff *skb) +{ + MIPV6_CALLPROC(mipv6_icmp_rcv)(skb); +} + +static inline int tcp_v6_get_mipv6_header_len(void) +{ + return MIPV6_HEADERS; +} + +static inline struct in6_addr * +mipv6_get_fake_hdr_daddr(struct in6_addr *hdaddr, struct in6_addr *daddr) +{ + return daddr; +} + +static inline void +addrconf_set_mipv6_mn_home(int ifindex, struct in6_addr *homeaddr, int plen, + struct in6_addr *homeagent, int plen2) +{ + MIPV6_CALLPROC(mipv6_set_home)(ifindex, homeaddr, plen, homeagent, plen2); +} + +static inline void addrconf_get_mipv6_home_address(struct in6_addr *saddr) +{ + MIPV6_CALLPROC(mipv6_get_home_address)(saddr); +} + +static inline struct ipv6_txoptions * +ip6_add_mipv6_txoptions(struct sock *sk, struct sk_buff *skb, + struct ipv6_txoptions *opt, struct flowi *fl, + struct dst_entry **dst) +{ + return MIPV6_CALLFUNC(mipv6_modify_txoptions, opt)(sk, skb, opt, fl, dst); + +} + +static inline void +ip6_mark_mipv6_packet(struct ipv6_txoptions *txopt, struct sk_buff *skb) +{ + struct inet6_skb_parm *opt; + if (txopt) { + opt = (struct inet6_skb_parm *)skb->cb; + opt->mipv6_flags = txopt->mipv6_flags; + } +} + +static inline void +ip6_free_mipv6_txoptions(struct ipv6_txoptions *opt, + struct ipv6_txoptions *orig_opt) +{ + if (opt && opt != orig_opt) + kfree(opt); +} + +#else /* USE_IPV6_MOBILITY */ + +#define mipv6_handle_dstopt ip6_tlvopt_unknown + +static inline int +ndisc_mip_mn_ha_probe(struct inet6_ifaddr *ifp, u8 *lladdr) +{ + return 0; +} + +static inline int ip6_mipv6_forward(struct sk_buff *skb) +{ + return 0; +} + +static inline int ndisc_mipv6_ra_rcv(struct sk_buff *skb, + struct ndisc_options *ndopts) +{ + return MIPV6_ADD_RTR; +} + +static inline int ipv6_chk_mip_home_addr(struct in6_addr *addr) +{ + return 0; +} + +static inline void ndisc_mipv6_change_router(int change_rtr) {} + +static inline void ndisc_check_mipv6_dad(struct in6_addr *target) {} + +static inline void icmpv6_swap_mipv6_addrs(struct sk_buff *skb) {} + +static inline void mipv6_icmp_rcv(struct sk_buff *skb) {} + +static inline int tcp_v6_get_mipv6_header_len(void) +{ + return 0; +} + +static inline struct in6_addr * +mipv6_get_fake_hdr_daddr(struct in6_addr *hdaddr, struct in6_addr *daddr) +{ + return hdaddr; +} + +static inline void +addrconf_set_mipv6_mn_home(int ifindex, struct in6_addr *homeaddr, int plen, + struct in6_addr *homeagent, int plen2) {} + +static inline void addrconf_get_mipv6_home_address(struct in6_addr *saddr) {} + +static inline struct ipv6_txoptions * +ip6_add_mipv6_txoptions(struct sock *sk, struct sk_buff *skb, + struct ipv6_txoptions *opt, struct flowi *fl, + struct dst_entry **dst) +{ + return opt; +} + +static inline void +ip6_mark_mipv6_packet(struct ipv6_txoptions *txopt, struct sk_buff *skb) {} + +static inline void +ip6_free_mipv6_txoptions(struct ipv6_txoptions *opt, + struct ipv6_txoptions *orig_opt) {} + +#endif /* USE_IPV6_MOBILITY */ +#endif /* __KERNEL__ */ +#endif /* _NET_MIPGLUE_H */ diff -uprN linux-2.4.25.old/include/net/mipv6.h linux-2.4.25/include/net/mipv6.h --- linux-2.4.25.old/include/net/mipv6.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.25/include/net/mipv6.h 2004-06-26 11:29:29.000000000 +0100 @@ -0,0 +1,258 @@ +/* + * Mobile IPv6 header-file + * + * Authors: + * Sami Kivisaari + * + * $Id$ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + */ + +#ifndef _NET_MIPV6_H +#define _NET_MIPV6_H + +#include +#include +#include + +/* + * + * Mobile IPv6 Protocol constants + * + */ +#define DHAAD_RETRIES 4 /* transmissions */ +#define INITIAL_BINDACK_TIMEOUT 1 /* seconds */ +#define INITIAL_DHAAD_TIMEOUT 3 /* seconds */ +#define INITIAL_SOLICIT_TIMER 3 /* seconds */ +#define MAX_BINDACK_TIMEOUT 32 /* seconds */ +#define MAX_NONCE_LIFE 240 /* seconds */ +#define MAX_TOKEN_LIFE 210 /* seconds */ +#define MAX_RR_BINDING_LIFE 420 /* seconds */ +#define MAX_UPDATE_RATE 3 /* 1/s (min delay=1s) */ +#define PREFIX_ADV_RETRIES 3 /* transmissions */ +#define PREFIX_ADV_TIMEOUT 3 /* seconds */ + +#define MAX_FAST_UPDATES 5 /* transmissions */ +#define MAX_PFX_ADV_DELAY 1000 /* seconds */ +#define SLOW_UPDATE_RATE 10 /* 1/10s (max delay=10s)*/ +#define INITIAL_BINDACK_DAD_TIMEOUT 2 /* seconds */ + +/* + * + * Mobile IPv6 (RFC 3775) Protocol configuration variable defaults + * + */ +#define DefHomeRtrAdvInterval 1000 /* seconds */ +#define DefMaxMobPfxAdvInterval 86400 /* seconds */ +#define DefMinDelayBetweenRAs 3 /* seconds (min 0.03) */ +#define DefMinMobPfxAdvInterval 600 /* seconds */ +#define DefInitialBindackTimeoutFirstReg 1.5 /* seconds */ + +/* This is not actually specified in the draft, but is needed to avoid + * prefix solicitation storm when valid lifetime of a prefix is smaller + * than MAX_PFX_ADV_DELAY + */ +#define MIN_PFX_SOL_DELAY 5 /* seconds */ + +/* Mobile IPv6 ICMP types */ +/* + * Official numbers from RFC 3775 + */ +#define MIPV6_DHAAD_REQUEST 144 +#define MIPV6_DHAAD_REPLY 145 +#define MIPV6_PREFIX_SOLICIT 146 +#define MIPV6_PREFIX_ADV 147 + +/* Binding update flag codes */ +#define MIPV6_BU_F_ACK 0x80 +#define MIPV6_BU_F_HOME 0x40 +#define MIPV6_BU_F_LLADDR 0x20 +#define MIPV6_BU_F_KEYMGM 0x10 + +/* Binding ackknowledgment flag codes */ +#define MIPV6_BA_F_KEYMGM 0x80 + +/* Binding error status */ +#define MIPV6_BE_HAO_WO_BINDING 1 +#define MIPV6_BE_UNKNOWN_MH_TYPE 2 + +/* Mobility Header */ +struct mipv6_mh +{ + __u8 payload; /* Payload Protocol */ + __u8 length; /* MH Length */ + __u8 type; /* MH Type */ + __u8 reserved; /* Reserved */ + __u16 checksum; /* Checksum */ + __u8 data[0]; /* Message specific data */ +} __attribute__ ((packed)); + +/* Mobility Header type */ +#define IPPROTO_MOBILITY 135 /* RFC 3775*/ +/* Mobility Header Message Types */ + +#define MIPV6_MH_BRR 0 +#define MIPV6_MH_HOTI 1 +#define MIPV6_MH_COTI 2 +#define MIPV6_MH_HOT 3 +#define MIPV6_MH_COT 4 +#define MIPV6_MH_BU 5 +#define MIPV6_MH_BA 6 +#define MIPV6_MH_BE 7 + +/* + * Status codes for Binding Acknowledgements + */ +#define SUCCESS 0 +#define REASON_UNSPECIFIED 128 +#define ADMINISTRATIVELY_PROHIBITED 129 +#define INSUFFICIENT_RESOURCES 130 +#define HOME_REGISTRATION_NOT_SUPPORTED 131 +#define NOT_HOME_SUBNET 132 +#define NOT_HA_FOR_MN 133 +#define DUPLICATE_ADDR_DETECT_FAIL 134 +#define SEQUENCE_NUMBER_OUT_OF_WINDOW 135 +#define EXPIRED_HOME_NONCE_INDEX 136 +#define EXPIRED_CAREOF_NONCE_INDEX 137 +#define EXPIRED_NONCES 138 +#define REG_TYPE_CHANGE_FORBIDDEN 139 +/* + * Values for mipv6_flags in struct inet6_skb_parm + */ + +#define MIPV6_RCV_TUNNEL 0x1 +#define MIPV6_SND_HAO 0x2 +#define MIPV6_SND_BU 0x4 + +/* + * Mobility Header Message structures + */ + +struct mipv6_mh_brr +{ + __u16 reserved; + /* Mobility options */ +} __attribute__ ((packed)); + +struct mipv6_mh_bu +{ + __u16 sequence; /* sequence number of BU */ + __u8 flags; /* flags */ + __u8 reserved; /* reserved bits */ + __u16 lifetime; /* lifetime of BU */ + /* Mobility options */ +} __attribute__ ((packed)); + +struct mipv6_mh_ba +{ + __u8 status; /* statuscode */ + __u8 reserved; /* reserved bits */ + __u16 sequence; /* sequence number of BA */ + __u16 lifetime; /* lifetime in CN's bcache */ + /* Mobility options */ +} __attribute__ ((packed)); + +struct mipv6_mh_be +{ + __u8 status; + __u8 reserved; + struct in6_addr home_addr; + /* Mobility options */ +} __attribute__ ((packed)); + +struct mipv6_mh_addr_ti +{ + __u16 reserved; /* Reserved */ + u_int8_t init_cookie[8]; /* HoT/CoT Init Cookie */ + /* Mobility options */ +} __attribute__ ((packed)); + +struct mipv6_mh_addr_test +{ + __u16 nonce_index; /* Home/Care-of Nonce Index */ + u_int8_t init_cookie[8]; /* HoT/CoT Init Cookie */ + u_int8_t kgen_token[8]; /* Home/Care-of key generation token */ + /* Mobility options */ +} __attribute__ ((packed)); + +/* + * Mobility Options for various MH types. + */ +#define MIPV6_OPT_PAD1 0x00 +#define MIPV6_OPT_PADN 0x01 +#define MIPV6_OPT_BIND_REFRESH_ADVICE 0x02 +#define MIPV6_OPT_ALTERNATE_COA 0x03 +#define MIPV6_OPT_NONCE_INDICES 0x04 +#define MIPV6_OPT_AUTH_DATA 0x05 + +#define MIPV6_SEQ_GT(x,y) \ + ((short int)(((__u16)(x)) - ((__u16)(y))) > 0) + +/* + * Mobility Option structures + */ + +struct mipv6_mo +{ + __u8 type; + __u8 length; + __u8 value[0]; /* type specific data */ +} __attribute__ ((packed)); + +struct mipv6_mo_pad1 +{ + __u8 type; +} __attribute__ ((packed)); + +struct mipv6_mo_padn +{ + __u8 type; + __u8 length; + __u8 data[0]; +} __attribute__ ((packed)); + +struct mipv6_mo_alt_coa +{ + __u8 type; + __u8 length; + struct in6_addr addr; /* alternate care-of-address */ +} __attribute__ ((packed)); + +struct mipv6_mo_nonce_indices +{ + __u8 type; + __u8 length; + __u16 home_nonce_i; /* Home Nonce Index */ + __u16 careof_nonce_i; /* Careof Nonce Index */ +} __attribute__ ((packed)); + +struct mipv6_mo_bauth_data +{ + __u8 type; + __u8 length; + __u8 data[0]; +} __attribute__ ((packed)); + +struct mipv6_mo_br_advice +{ + __u8 type; + __u8 length; + __u16 refresh_interval; /* Refresh Interval */ +} __attribute__ ((packed)); + +/* + * Home Address Destination Option structure + */ +struct mipv6_dstopt_homeaddr +{ + __u8 type; /* type-code for option */ + __u8 length; /* option length */ + struct in6_addr addr; /* home address */ +} __attribute__ ((packed)); + +#endif /* _NET_MIPV6_H */ diff -uprN linux-2.4.25.old/include/net/ndisc.h linux-2.4.25/include/net/ndisc.h --- linux-2.4.25.old/include/net/ndisc.h 2002-11-28 23:53:15.000000000 +0000 +++ linux-2.4.25/include/net/ndisc.h 2004-06-26 11:29:29.000000000 +0100 @@ -21,6 +21,10 @@ #define ND_OPT_REDIRECT_HDR 4 #define ND_OPT_MTU 5 +/* Mobile IPv6 specific ndisc options */ +#define ND_OPT_RTR_ADV_INTERVAL 7 +#define ND_OPT_HOME_AGENT_INFO 8 + #define MAX_RTR_SOLICITATION_DELAY HZ #define ND_REACHABLE_TIME (30*HZ) @@ -57,7 +61,7 @@ struct nd_opt_hdr { } __attribute__((__packed__)); struct ndisc_options { - struct nd_opt_hdr *nd_opt_array[7]; + struct nd_opt_hdr *nd_opt_array[10]; struct nd_opt_hdr *nd_opt_piend; }; @@ -67,6 +71,8 @@ struct ndisc_options { #define nd_opts_pi_end nd_opt_piend #define nd_opts_rh nd_opt_array[ND_OPT_REDIRECT_HDR] #define nd_opts_mtu nd_opt_array[ND_OPT_MTU] +#define nd_opts_rai nd_opt_array[ND_OPT_RTR_ADV_INTERVAL] +#define nd_opts_hai nd_opt_array[ND_OPT_HOME_AGENT_INFO] extern struct nd_opt_hdr *ndisc_next_option(struct nd_opt_hdr *cur, struct nd_opt_hdr *end); extern struct ndisc_options *ndisc_parse_options(u8 *opt, int opt_len, struct ndisc_options *ndopts); @@ -83,6 +89,15 @@ extern void ndisc_send_ns(struct net_d struct in6_addr *daddr, struct in6_addr *saddr); +extern void ndisc_send_na(struct net_device *dev, + struct neighbour *neigh, + struct in6_addr *daddr, + struct in6_addr *solicited_addr, + int router, + int solicited, + int override, + int inc_opt); + extern void ndisc_send_rs(struct net_device *dev, struct in6_addr *saddr, struct in6_addr *daddr); diff -uprN linux-2.4.25.old/include/net/sock.h linux-2.4.25/include/net/sock.h --- linux-2.4.25.old/include/net/sock.h 2004-02-18 13:36:32.000000000 +0000 +++ linux-2.4.25/include/net/sock.h 2004-06-26 11:29:30.000000000 +0100 @@ -149,7 +149,9 @@ struct ipv6_pinfo { struct in6_addr rcv_saddr; struct in6_addr daddr; struct in6_addr *daddr_cache; - +#if defined(CONFIG_IPV6_SUBTREES) + struct in6_addr *saddr_cache; +#endif __u32 flow_label; __u32 frag_size; int hop_limit; diff -uprN linux-2.4.25.old/net/Makefile linux-2.4.25/net/Makefile --- linux-2.4.25.old/net/Makefile 2004-06-26 11:22:00.000000000 +0100 +++ linux-2.4.25/net/Makefile 2004-06-26 11:29:30.000000000 +0100 @@ -7,7 +7,7 @@ O_TARGET := network.o -mod-subdirs := ipv4/netfilter ipv6/netfilter ipx irda bluetooth atm netlink sched core sctp +mod-subdirs := ipv4/netfilter ipv6/netfilter ipx irda bluetooth atm netlink sched core sctp ipv6 export-objs := netsyms.o subdir-y := core ethernet @@ -25,6 +25,7 @@ subdir-$(CONFIG_IP_SCTP) += sctp ifneq ($(CONFIG_IPV6),n) ifneq ($(CONFIG_IPV6),) subdir-$(CONFIG_NETFILTER) += ipv6/netfilter +subdir-$(CONFIG_IPV6_MOBILITY) += ipv6/mobile_ip6 endif endif diff -uprN linux-2.4.25.old/net/core/neighbour.c linux-2.4.25/net/core/neighbour.c --- linux-2.4.25.old/net/core/neighbour.c 2004-02-18 13:36:32.000000000 +0000 +++ linux-2.4.25/net/core/neighbour.c 2004-06-26 11:29:30.000000000 +0100 @@ -386,7 +386,7 @@ struct pneigh_entry * pneigh_lookup(stru if (!creat) return NULL; - n = kmalloc(sizeof(*n) + key_len, GFP_KERNEL); + n = kmalloc(sizeof(*n) + key_len, GFP_ATOMIC); if (n == NULL) return NULL; diff -uprN linux-2.4.25.old/net/ipv6/Config.in linux-2.4.25/net/ipv6/Config.in --- linux-2.4.25.old/net/ipv6/Config.in 2001-12-21 17:42:05.000000000 +0000 +++ linux-2.4.25/net/ipv6/Config.in 2004-06-26 11:29:30.000000000 +0100 @@ -1,10 +1,16 @@ # # IPv6 configuration # - +bool ' IPv6: routing by source address (EXPERIMENTAL)' CONFIG_IPV6_SUBTREES #bool ' IPv6: flow policy support' CONFIG_RT6_POLICY #bool ' IPv6: firewall support' CONFIG_IPV6_FIREWALL +if [ "$CONFIG_IPV6" != "n" ]; then + dep_tristate ' IPv6: IPv6 over IPv6 Tunneling (EXPERIMENTAL)' CONFIG_IPV6_TUNNEL $CONFIG_IPV6 +fi + +source net/ipv6/mobile_ip6/Config.in + if [ "$CONFIG_NETFILTER" != "n" ]; then source net/ipv6/netfilter/Config.in fi diff -uprN linux-2.4.25.old/net/ipv6/Makefile linux-2.4.25/net/ipv6/Makefile --- linux-2.4.25.old/net/ipv6/Makefile 2003-11-28 18:26:21.000000000 +0000 +++ linux-2.4.25/net/ipv6/Makefile 2004-06-26 11:29:30.000000000 +0100 @@ -6,18 +6,28 @@ # unless it's something special (ie not a .c file). # +export-objs := ipv6_syms.o ipv6_tunnel.o -O_TARGET := ipv6.o +#list-multi := ipv6.o ipv6_tunnel.o -obj-y := af_inet6.o anycast.o ip6_output.o ip6_input.o addrconf.o sit.o \ - route.o ip6_fib.o ipv6_sockglue.o ndisc.o udp.o raw.o \ - protocol.o icmp.o mcast.o reassembly.o tcp_ipv6.o \ - exthdrs.o sysctl_net_ipv6.o datagram.o proc.o \ - ip6_flowlabel.o ipv6_syms.o +ipv6-objs := af_inet6.o anycast.o ip6_output.o ip6_input.o addrconf.o \ + sit.o route.o ip6_fib.o ipv6_sockglue.o ndisc.o udp.o \ + raw.o protocol.o icmp.o mcast.o reassembly.o tcp_ipv6.o \ + exthdrs.o sysctl_net_ipv6.o datagram.o proc.o \ + ip6_flowlabel.o ipv6_syms.o + +ifneq ($(CONFIG_IPV6_MOBILITY),n) +ifneq ($(CONFIG_IPV6_MOBILITY),) +ipv6-objs += mipglue.o +endif +endif -export-objs := ipv6_syms.o +obj-$(CONFIG_IPV6) += ipv6.o +obj-$(CONFIG_IPV6_TUNNEL) += ipv6_tunnel.o + +ipv6.o: $(ipv6-objs) + $(LD) -r -o $@ $(ipv6-objs) -obj-m := $(O_TARGET) #obj-$(CONFIG_IPV6_FIREWALL) += ip6_fw.o diff -uprN linux-2.4.25.old/net/ipv6/addrconf.c linux-2.4.25/net/ipv6/addrconf.c --- linux-2.4.25.old/net/ipv6/addrconf.c 2003-11-28 18:26:21.000000000 +0000 +++ linux-2.4.25/net/ipv6/addrconf.c 2004-06-26 11:29:30.000000000 +0100 @@ -68,6 +68,8 @@ #include +#include + #define IPV6_MAX_ADDRESSES 16 /* Set to 3 to get tracing... */ @@ -103,9 +105,9 @@ static spinlock_t addrconf_verify_lock = static int addrconf_ifdown(struct net_device *dev, int how); -static void addrconf_dad_start(struct inet6_ifaddr *ifp, int flags); +void addrconf_dad_start(struct inet6_ifaddr *ifp, int flags); static void addrconf_dad_timer(unsigned long data); -static void addrconf_dad_completed(struct inet6_ifaddr *ifp); +void addrconf_dad_completed(struct inet6_ifaddr *ifp); static void addrconf_rs_timer(unsigned long data); static void ipv6_ifa_notify(int event, struct inet6_ifaddr *ifa); @@ -330,38 +332,6 @@ static struct inet6_dev * ipv6_find_idev return idev; } -void ipv6_addr_prefix(struct in6_addr *prefix, - struct in6_addr *addr, int prefix_len) -{ - unsigned long mask; - int ncopy, nbits; - - memset(prefix, 0, sizeof(*prefix)); - - if (prefix_len <= 0) - return; - if (prefix_len > 128) - prefix_len = 128; - - ncopy = prefix_len / 32; - switch (ncopy) { - case 4: prefix->s6_addr32[3] = addr->s6_addr32[3]; - case 3: prefix->s6_addr32[2] = addr->s6_addr32[2]; - case 2: prefix->s6_addr32[1] = addr->s6_addr32[1]; - case 1: prefix->s6_addr32[0] = addr->s6_addr32[0]; - case 0: break; - } - nbits = prefix_len % 32; - if (nbits == 0) - return; - - mask = ~((1 << (32 - nbits)) - 1); - mask = htonl(mask); - - prefix->s6_addr32[ncopy] = addr->s6_addr32[ncopy] & mask; -} - - static void dev_forward_change(struct inet6_dev *idev) { struct net_device *dev; @@ -513,7 +483,7 @@ ipv6_add_addr(struct inet6_dev *idev, co /* This function wants to get referenced ifp and releases it before return */ -static void ipv6_del_addr(struct inet6_ifaddr *ifp) +void ipv6_del_addr(struct inet6_ifaddr *ifp) { struct inet6_ifaddr *ifa, **ifap; struct inet6_dev *idev = ifp->idev; @@ -662,6 +632,12 @@ out: if (match) in6_ifa_put(match); + /* The home address is always used as source address in + * MIPL mobile IPv6 + */ + if (scope != IFA_HOST && scope != IFA_LINK) + addrconf_get_mipv6_home_address(saddr); + return err; } @@ -815,7 +791,7 @@ void addrconf_leave_solict(struct net_de } -static int ipv6_generate_eui64(u8 *eui, struct net_device *dev) +int ipv6_generate_eui64(u8 *eui, struct net_device *dev) { switch (dev->type) { case ARPHRD_ETHER: @@ -840,7 +816,7 @@ static int ipv6_generate_eui64(u8 *eui, return -1; } -static int ipv6_inherit_eui64(u8 *eui, struct inet6_dev *idev) +int ipv6_inherit_eui64(u8 *eui, struct inet6_dev *idev) { int err = -1; struct inet6_ifaddr *ifp; @@ -1407,6 +1383,24 @@ static void addrconf_sit_config(struct n sit_route_add(dev); } +/** + * addrconf_ipv6_tunnel_config - configure IPv6 tunnel device + * @dev: tunnel device + **/ + +static void addrconf_ipv6_tunnel_config(struct net_device *dev) +{ + struct inet6_dev *idev; + + ASSERT_RTNL(); + + /* Assign inet6_dev structure to tunnel device */ + if ((idev = ipv6_find_idev(dev)) == NULL) { + printk(KERN_DEBUG "init ipv6 tunnel: add_dev failed\n"); + return; + } +} + int addrconf_notify(struct notifier_block *this, unsigned long event, void * data) @@ -1421,6 +1415,10 @@ int addrconf_notify(struct notifier_bloc addrconf_sit_config(dev); break; + case ARPHRD_TUNNEL6: + addrconf_ipv6_tunnel_config(dev); + break; + case ARPHRD_LOOPBACK: init_loopback(dev); break; @@ -1602,7 +1600,7 @@ out: /* * Duplicate Address Detection */ -static void addrconf_dad_start(struct inet6_ifaddr *ifp, int flags) +void addrconf_dad_start(struct inet6_ifaddr *ifp, int flags) { struct net_device *dev; unsigned long rand_num; @@ -1667,7 +1665,7 @@ static void addrconf_dad_timer(unsigned in6_ifa_put(ifp); } -static void addrconf_dad_completed(struct inet6_ifaddr *ifp) +void addrconf_dad_completed(struct inet6_ifaddr *ifp) { struct net_device * dev = ifp->idev->dev; @@ -1676,7 +1674,7 @@ static void addrconf_dad_completed(struc */ ipv6_ifa_notify(RTM_NEWADDR, ifp); - + notifier_call_chain(&inet6addr_chain,NETDEV_UP,ifp); /* If added prefix is link local and forwarding is off, start sending router solicitations. */ @@ -1877,8 +1875,20 @@ inet6_rtm_newaddr(struct sk_buff *skb, s if (rta[IFA_LOCAL-1]) { if (pfx && memcmp(pfx, RTA_DATA(rta[IFA_LOCAL-1]), sizeof(*pfx))) return -EINVAL; + if (ifm->ifa_flags & IFA_F_HOMEADDR && !rta[IFA_HOMEAGENT-1]) + return -EINVAL; pfx = RTA_DATA(rta[IFA_LOCAL-1]); } + if (rta[IFA_HOMEAGENT-1]) { + struct in6_addr *ha; + if (pfx == NULL || !(ifm->ifa_flags & IFA_F_HOMEADDR)) + return -EINVAL; + if (RTA_PAYLOAD(rta[IFA_HOMEAGENT-1]) < sizeof(*ha)) + return -EINVAL; + ha = RTA_DATA(rta[IFA_HOMEAGENT-1]); + addrconf_set_mipv6_mn_home(ifm->ifa_index, pfx, ifm->ifa_prefixlen, + ha, ifm->ifa_prefixlen); + } if (pfx == NULL) return -EINVAL; diff -uprN linux-2.4.25.old/net/ipv6/af_inet6.c linux-2.4.25/net/ipv6/af_inet6.c --- linux-2.4.25.old/net/ipv6/af_inet6.c 2003-11-28 18:26:21.000000000 +0000 +++ linux-2.4.25/net/ipv6/af_inet6.c 2004-06-26 11:29:30.000000000 +0100 @@ -58,6 +58,9 @@ #include #include #include +#ifdef CONFIG_IPV6_TUNNEL +#include +#endif #include #include @@ -646,6 +649,11 @@ static int __init inet6_init(void) err = ndisc_init(&inet6_family_ops); if (err) goto ndisc_fail; +#ifdef CONFIG_IPV6_TUNNEL + err = ip6_tunnel_init(); + if (err) + goto ip6_tunnel_fail; +#endif err = igmp6_init(&inet6_family_ops); if (err) goto igmp_fail; @@ -698,6 +706,10 @@ proc_raw6_fail: #endif igmp_fail: ndisc_cleanup(); +#ifdef CONFIG_IPV6_TUNNEL + ip6_tunnel_cleanup(); +ip6_tunnel_fail: +#endif ndisc_fail: icmpv6_cleanup(); icmp_fail: @@ -730,6 +742,9 @@ static void inet6_exit(void) ip6_route_cleanup(); ipv6_packet_cleanup(); igmp6_cleanup(); +#ifdef CONFIG_IPV6_TUNNEL + ip6_tunnel_cleanup(); +#endif ndisc_cleanup(); icmpv6_cleanup(); #ifdef CONFIG_SYSCTL diff -uprN linux-2.4.25.old/net/ipv6/exthdrs.c linux-2.4.25/net/ipv6/exthdrs.c --- linux-2.4.25.old/net/ipv6/exthdrs.c 2003-08-25 12:44:44.000000000 +0100 +++ linux-2.4.25/net/ipv6/exthdrs.c 2004-06-26 11:29:30.000000000 +0100 @@ -41,6 +41,9 @@ #include #include +#include +#include + #include /* @@ -160,7 +163,8 @@ bad: *****************************/ struct tlvtype_proc tlvprocdestopt_lst[] = { - /* No destination options are defined now */ + /* Mobility Support destination options */ + {MIPV6_TLV_HOMEADDR, mipv6_handle_dstopt}, {-1, NULL} }; @@ -210,6 +214,7 @@ static int ipv6_routing_header(struct sk struct ipv6_rt_hdr *hdr; struct rt0_hdr *rthdr; + struct rt2_hdr *rt2hdr; if (!pskb_may_pull(skb, (skb->h.raw-skb->data)+8) || !pskb_may_pull(skb, (skb->h.raw-skb->data)+((skb->h.raw[1]+1)<<3))) { @@ -225,17 +230,25 @@ static int ipv6_routing_header(struct sk kfree_skb(skb); return -1; } - + /* Silently discard invalid packets containing RTH type 2 */ + if (hdr->type == IPV6_SRCRT_TYPE_2 && + (hdr->hdrlen != 2 || hdr->segments_left != 1)) { + kfree_skb(skb); + return -1; + } looped_back: if (hdr->segments_left == 0) { - opt->srcrt = skb->h.raw - skb->nh.raw; + if (hdr->type == IPV6_SRCRT_TYPE_0) + opt->srcrt = skb->h.raw - skb->nh.raw; + else if (hdr->type == IPV6_SRCRT_TYPE_2) + opt->srcrt2 = skb->h.raw - skb->nh.raw; skb->h.raw += (hdr->hdrlen + 1) << 3; opt->dst0 = opt->dst1; opt->dst1 = 0; return (&hdr->nexthdr) - skb->nh.raw; } - if (hdr->type != IPV6_SRCRT_TYPE_0) { + if (hdr->type != IPV6_SRCRT_TYPE_0 && hdr->type != IPV6_SRCRT_TYPE_2) { icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, (&hdr->type) - skb->nh.raw); return -1; } @@ -275,9 +288,20 @@ looped_back: i = n - --hdr->segments_left; - rthdr = (struct rt0_hdr *) hdr; - addr = rthdr->addr; - addr += i - 1; + if (hdr->type == IPV6_SRCRT_TYPE_0) { + rthdr = (struct rt0_hdr *) hdr; + addr = rthdr->addr; + addr += i - 1; + } else { + /* check that address is this node's home address */ + rt2hdr = (struct rt2_hdr *) hdr; + addr = &rt2hdr->addr; + if (!ipv6_chk_addr(addr, NULL) || + !ipv6_chk_mip_home_addr(addr)) { + kfree_skb(skb); + return -1; + } + } addr_type = ipv6_addr_type(addr); @@ -330,6 +354,10 @@ looped_back: temporary (or permanent) backdoor. If listening socket set IPV6_RTHDR to 2, then we invert header. --ANK (980729) + + By the Mobile IPv6 specification Type 2 routing header MUST NOT be + inverted. + --AJT (20020917) */ struct ipv6_txoptions * @@ -352,6 +380,18 @@ ipv6_invert_rthdr(struct sock *sk, struc struct ipv6_txoptions *opt; int hdrlen = ipv6_optlen(hdr); + if (hdr->type == IPV6_SRCRT_TYPE_2) { + opt = sock_kmalloc(sk, sizeof(*opt) + hdrlen, GFP_ATOMIC); + if (opt == NULL) + return NULL; + memset(opt, 0, sizeof(*opt)); + opt->tot_len = sizeof(*opt) + hdrlen; + opt->srcrt = (void*)(opt+1); + opt->opt_nflen = hdrlen; + memcpy(opt->srcrt, hdr, sizeof(struct rt2_hdr)); + return opt; + } + if (hdr->segments_left || hdr->type != IPV6_SRCRT_TYPE_0 || hdr->hdrlen & 0x01) @@ -622,8 +662,18 @@ u8 *ipv6_build_nfrag_opts(struct sk_buff if (opt) { if (opt->dst0opt) prev_hdr = ipv6_build_exthdr(skb, prev_hdr, NEXTHDR_DEST, opt->dst0opt); - if (opt->srcrt) - prev_hdr = ipv6_build_rthdr(skb, prev_hdr, opt->srcrt, daddr); + if (opt->srcrt) { + if (opt->srcrt2) { + struct in6_addr *rt2_hop = &((struct rt2_hdr *)opt->srcrt2)->addr; + prev_hdr = ipv6_build_rthdr(skb, prev_hdr, opt->srcrt, rt2_hop); + } else + prev_hdr = ipv6_build_rthdr(skb, prev_hdr, opt->srcrt, daddr); + } + if (opt->srcrt2) { + struct inet6_skb_parm *parm = (struct inet6_skb_parm *)skb->cb; + ipv6_addr_copy(&parm->hoa, daddr); + prev_hdr = ipv6_build_rthdr(skb, prev_hdr, opt->srcrt2, daddr); + } } return prev_hdr; } @@ -684,6 +734,11 @@ void ipv6_push_nfrag_opts(struct sk_buff u8 *proto, struct in6_addr **daddr) { + if (opt->srcrt2) { + struct inet6_skb_parm *parm = (struct inet6_skb_parm *)skb->cb; + ipv6_addr_copy(&parm->hoa, *daddr); + ipv6_push_rthdr(skb, proto, opt->srcrt2, daddr); + } if (opt->srcrt) ipv6_push_rthdr(skb, proto, opt->srcrt, daddr); if (opt->dst0opt) @@ -719,6 +774,8 @@ ipv6_dup_options(struct sock *sk, struct *((char**)&opt2->auth) += dif; if (opt2->srcrt) *((char**)&opt2->srcrt) += dif; + if (opt2->srcrt2) + *((char**)&opt2->srcrt2) += dif; } return opt2; } diff -uprN linux-2.4.25.old/net/ipv6/icmp.c linux-2.4.25/net/ipv6/icmp.c --- linux-2.4.25.old/net/ipv6/icmp.c 2003-11-28 18:26:21.000000000 +0000 +++ linux-2.4.25/net/ipv6/icmp.c 2004-06-26 11:29:30.000000000 +0100 @@ -61,6 +61,8 @@ #include #include +#include + #include #include @@ -364,6 +366,8 @@ void icmpv6_send(struct sk_buff *skb, in msg.len = len; + icmpv6_swap_mipv6_addrs(skb); + ip6_build_xmit(sk, icmpv6_getfrag, &msg, &fl, len, NULL, -1, MSG_DONTWAIT); if (type >= ICMPV6_DEST_UNREACH && type <= ICMPV6_PARAMPROB) @@ -562,13 +566,13 @@ int icmpv6_rcv(struct sk_buff *skb) rt6_pmtu_discovery(&orig_hdr->daddr, &orig_hdr->saddr, dev, ntohl(hdr->icmp6_mtu)); - /* - * Drop through to notify - */ + icmpv6_notify(skb, type, hdr->icmp6_code, hdr->icmp6_mtu); + break; case ICMPV6_DEST_UNREACH: - case ICMPV6_TIME_EXCEED: case ICMPV6_PARAMPROB: + mipv6_icmp_rcv(skb); + case ICMPV6_TIME_EXCEED: icmpv6_notify(skb, type, hdr->icmp6_code, hdr->icmp6_mtu); break; @@ -597,6 +601,13 @@ int icmpv6_rcv(struct sk_buff *skb) case ICMPV6_MGM_REDUCTION: break; + case MIPV6_DHAAD_REQUEST: + case MIPV6_DHAAD_REPLY: + case MIPV6_PREFIX_SOLICIT: + case MIPV6_PREFIX_ADV: + mipv6_icmp_rcv(skb); + break; + default: if (net_ratelimit()) printk(KERN_DEBUG "icmpv6: msg of unkown type\n"); diff -uprN linux-2.4.25.old/net/ipv6/ip6_fib.c linux-2.4.25/net/ipv6/ip6_fib.c --- linux-2.4.25.old/net/ipv6/ip6_fib.c 2003-08-25 12:44:44.000000000 +0100 +++ linux-2.4.25/net/ipv6/ip6_fib.c 2004-06-26 11:29:30.000000000 +0100 @@ -18,6 +18,7 @@ * Yuji SEKIYA @USAGI: Support default route on router node; * remove ip6_null_entry from the top of * routing table. + * Ville Nuorvala: Fixes to source address based routing */ #include #include @@ -40,7 +41,6 @@ #include #define RT6_DEBUG 2 -#undef CONFIG_IPV6_SUBTREES #if RT6_DEBUG >= 3 #define RT6_TRACE(x...) printk(KERN_DEBUG x) @@ -500,6 +500,8 @@ static __inline__ void fib6_start_gc(str mod_timer(&ip6_fib_timer, jiffies + ip6_rt_gc_interval); } +static struct rt6_info * fib6_find_prefix(struct fib6_node *fn); + /* * Add routing information to the routing tree. * / @@ -508,17 +510,19 @@ static __inline__ void fib6_start_gc(str int fib6_add(struct fib6_node *root, struct rt6_info *rt, struct nlmsghdr *nlh) { - struct fib6_node *fn; + struct fib6_node *fn = root; int err = -ENOMEM; - fn = fib6_add_1(root, &rt->rt6i_dst.addr, sizeof(struct in6_addr), - rt->rt6i_dst.plen, (u8*) &rt->rt6i_dst - (u8*) rt); +#ifdef CONFIG_IPV6_SUBTREES + struct fib6_node *pn = NULL; + fn = fib6_add_1(root, &rt->rt6i_src.addr, sizeof(struct in6_addr), + rt->rt6i_src.plen, (u8*) &rt->rt6i_src - (u8*) rt); + if (fn == NULL) goto out; -#ifdef CONFIG_IPV6_SUBTREES - if (rt->rt6i_src.plen) { + if (rt->rt6i_dst.plen) { struct fib6_node *sn; if (fn->subtree == NULL) { @@ -546,9 +550,9 @@ int fib6_add(struct fib6_node *root, str /* Now add the first leaf node to new subtree */ - sn = fib6_add_1(sfn, &rt->rt6i_src.addr, - sizeof(struct in6_addr), rt->rt6i_src.plen, - (u8*) &rt->rt6i_src - (u8*) rt); + sn = fib6_add_1(sfn, &rt->rt6i_dst.addr, + sizeof(struct in6_addr), rt->rt6i_dst.plen, + (u8*) &rt->rt6i_dst - (u8*) rt); if (sn == NULL) { /* If it is failed, discard just allocated @@ -562,21 +566,30 @@ int fib6_add(struct fib6_node *root, str /* Now link new subtree to main tree */ sfn->parent = fn; fn->subtree = sfn; - if (fn->leaf == NULL) { - fn->leaf = rt; - atomic_inc(&rt->rt6i_ref); - } } else { - sn = fib6_add_1(fn->subtree, &rt->rt6i_src.addr, - sizeof(struct in6_addr), rt->rt6i_src.plen, - (u8*) &rt->rt6i_src - (u8*) rt); + sn = fib6_add_1(fn->subtree, &rt->rt6i_dst.addr, + sizeof(struct in6_addr), rt->rt6i_dst.plen, + (u8*) &rt->rt6i_dst - (u8*) rt); if (sn == NULL) goto st_failure; } + /* fib6_add_1 might have cleared the old leaf pointer */ + if (fn->leaf == NULL) { + fn->leaf = rt; + atomic_inc(&rt->rt6i_ref); + } + + pn = fn; fn = sn; } +#else + fn = fib6_add_1(root, &rt->rt6i_dst.addr, sizeof(struct in6_addr), + rt->rt6i_dst.plen, (u8*) &rt->rt6i_dst - (u8*) rt); + + if (fn == NULL) + goto out; #endif err = fib6_add_rt2node(fn, rt, nlh); @@ -588,8 +601,30 @@ int fib6_add(struct fib6_node *root, str } out: - if (err) + if (err) { +#ifdef CONFIG_IPV6_SUBTREES + + /* If fib6_add_1 has cleared the old leaf pointer in the + super-tree leaf node, we have to find a new one for it. + + This situation will never arise in the sub-tree since + the node will at least have the route that caused + fib6_add_rt2node to fail. + */ + + if (pn && !(pn->fn_flags & RTN_RTINFO)) { + pn->leaf = fib6_find_prefix(pn); +#if RT6_DEBUG >= 2 + if (!pn->leaf) { + BUG_TRAP(pn->leaf); + pn->leaf = &ip6_null_entry; + } +#endif + atomic_inc(&pn->leaf->rt6i_ref); + } +#endif dst_free(&rt->u.dst); + } return err; #ifdef CONFIG_IPV6_SUBTREES @@ -597,8 +632,8 @@ out: is orphan. If it is, shoot it. */ st_failure: - if (fn && !(fn->fn_flags&RTN_RTINFO|RTN_ROOT)) - fib_repair_tree(fn); + if (fn && !(fn->fn_flags & (RTN_RTINFO | RTN_ROOT))) + fib6_repair_tree(fn); dst_free(&rt->u.dst); return err; #endif @@ -641,22 +676,28 @@ static struct fib6_node * fib6_lookup_1( break; } - while ((fn->fn_flags & RTN_ROOT) == 0) { + for (;;) { #ifdef CONFIG_IPV6_SUBTREES if (fn->subtree) { - struct fib6_node *st; - struct lookup_args *narg; - - narg = args + 1; - - if (narg->addr) { - st = fib6_lookup_1(fn->subtree, narg); + struct rt6key *key; - if (st && !(st->fn_flags & RTN_ROOT)) - return st; + key = (struct rt6key *) ((u8 *) fn->leaf + + args->offset); + + if (addr_match(&key->addr, args->addr, key->plen)) { + struct fib6_node *st; + struct lookup_args *narg = args + 1; + if (!ipv6_addr_any(narg->addr)) { + st = fib6_lookup_1(fn->subtree, narg); + + if (st && !(st->fn_flags & RTN_ROOT)) + return st; + } } } #endif + if (fn->fn_flags & RTN_ROOT) + break; if (fn->fn_flags & RTN_RTINFO) { struct rt6key *key; @@ -680,13 +721,22 @@ struct fib6_node * fib6_lookup(struct fi struct lookup_args args[2]; struct rt6_info *rt = NULL; struct fib6_node *fn; +#ifdef CONFIG_IPV6_SUBTREES + struct in6_addr saddr_buf; +#endif +#ifdef CONFIG_IPV6_SUBTREES + if (saddr == NULL) { + memset(&saddr_buf, 0, sizeof(struct in6_addr)); + saddr = &saddr_buf; + } + args[0].offset = (u8*) &rt->rt6i_src - (u8*) rt; + args[0].addr = saddr; + args[1].offset = (u8*) &rt->rt6i_dst - (u8*) rt; + args[1].addr = daddr; +#else args[0].offset = (u8*) &rt->rt6i_dst - (u8*) rt; args[0].addr = daddr; - -#ifdef CONFIG_IPV6_SUBTREES - args[1].offset = (u8*) &rt->rt6i_src - (u8*) rt; - args[1].addr = saddr; #endif fn = fib6_lookup_1(root, args); @@ -739,19 +789,25 @@ struct fib6_node * fib6_locate(struct fi { struct rt6_info *rt = NULL; struct fib6_node *fn; - - fn = fib6_locate_1(root, daddr, dst_len, - (u8*) &rt->rt6i_dst - (u8*) rt); - #ifdef CONFIG_IPV6_SUBTREES - if (src_len) { - BUG_TRAP(saddr!=NULL); - if (fn == NULL) - fn = fn->subtree; + struct in6_addr saddr_buf; + + if (saddr == NULL) { + memset(&saddr_buf, 0, sizeof(struct in6_addr)); + saddr = &saddr_buf; + } + fn = fib6_locate_1(root, saddr, src_len, + (u8*) &rt->rt6i_src - (u8*) rt); + if (dst_len) { if (fn) - fn = fib6_locate_1(fn, saddr, src_len, - (u8*) &rt->rt6i_src - (u8*) rt); + fn = fib6_locate_1(fn->subtree, daddr, dst_len, + (u8*) &rt->rt6i_dst - (u8*) rt); + else + return NULL; } +#else + fn = fib6_locate_1(root, daddr, dst_len, + (u8*) &rt->rt6i_dst - (u8*) rt); #endif if (fn && fn->fn_flags&RTN_RTINFO) @@ -939,7 +995,7 @@ static void fib6_del_route(struct fib6_n } fn = fn->parent; } - /* No more references are possiible at this point. */ + /* No more references are possible at this point. */ if (atomic_read(&rt->rt6i_ref) != 1) BUG(); } diff -uprN linux-2.4.25.old/net/ipv6/ip6_input.c linux-2.4.25/net/ipv6/ip6_input.c --- linux-2.4.25.old/net/ipv6/ip6_input.c 2003-08-25 12:44:44.000000000 +0100 +++ linux-2.4.25/net/ipv6/ip6_input.c 2004-06-26 11:29:30.000000000 +0100 @@ -40,13 +40,42 @@ #include #include +static inline int ip6_proxy_chk(struct sk_buff *skb) +{ + struct ipv6hdr *hdr = skb->nh.ipv6h; - + if (ipv6_addr_type(&hdr->daddr)&IPV6_ADDR_UNICAST && + pneigh_lookup(&nd_tbl, &hdr->daddr, skb->dev, 0)) { + u8 nexthdr = hdr->nexthdr; + int offset; + struct icmp6hdr msg; + + if (ipv6_ext_hdr(nexthdr)) { + offset = ipv6_skip_exthdr(skb, sizeof(*hdr), &nexthdr, + skb->len - sizeof(*hdr)); + if (offset < 0) + return 0; + } else + offset = sizeof(*hdr); + + /* capture unicast NUD probes on behalf of the proxied node */ + + if (nexthdr == IPPROTO_ICMPV6 && + !skb_copy_bits(skb, offset, &msg, sizeof(msg)) && + msg.icmp6_type == NDISC_NEIGHBOUR_SOLICITATION) { + return 1; + } + } + return 0; +} + static inline int ip6_rcv_finish( struct sk_buff *skb) { - if (skb->dst == NULL) - ip6_route_input(skb); - + if (skb->dst == NULL) { + if (ip6_proxy_chk(skb)) + return ip6_input(skb); + ip6_route_input(skb); + } return skb->dst->input(skb); } diff -uprN linux-2.4.25.old/net/ipv6/ip6_output.c linux-2.4.25/net/ipv6/ip6_output.c --- linux-2.4.25.old/net/ipv6/ip6_output.c 2003-08-25 12:44:44.000000000 +0100 +++ linux-2.4.25/net/ipv6/ip6_output.c 2004-06-26 11:29:30.000000000 +0100 @@ -50,6 +50,8 @@ #include #include +#include + static __inline__ void ipv6_select_ident(struct sk_buff *skb, struct frag_hdr *fhdr) { static u32 ipv6_fragmentation_id = 1; @@ -194,7 +196,14 @@ int ip6_xmit(struct sock *sk, struct sk_ u8 proto = fl->proto; int seg_len = skb->len; int hlimit; + int retval; + struct ipv6_txoptions *orig_opt = opt; + + opt = ip6_add_mipv6_txoptions(sk, skb, orig_opt, fl, &dst); + if(orig_opt && !opt) + return -ENOMEM; + if (opt) { int head_room; @@ -209,8 +218,11 @@ int ip6_xmit(struct sock *sk, struct sk_ struct sk_buff *skb2 = skb_realloc_headroom(skb, head_room); kfree_skb(skb); skb = skb2; - if (skb == NULL) + if (skb == NULL) { + ip6_free_mipv6_txoptions(opt, orig_opt); + return -ENOBUFS; + } if (sk) skb_set_owner_w(skb, sk); } @@ -242,7 +254,10 @@ int ip6_xmit(struct sock *sk, struct sk_ if (skb->len <= dst->pmtu) { IP6_INC_STATS(Ip6OutRequests); - return NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, dst->dev, ip6_maybe_reroute); + ip6_mark_mipv6_packet(opt, skb); + retval = NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, dst->dev, ip6_maybe_reroute); + ip6_free_mipv6_txoptions(opt, orig_opt); + return retval; } if (net_ratelimit()) @@ -250,6 +265,9 @@ int ip6_xmit(struct sock *sk, struct sk_ skb->dev = dst->dev; icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, dst->pmtu, skb->dev); kfree_skb(skb); + + ip6_free_mipv6_txoptions(opt, orig_opt); + return -EMSGSIZE; } @@ -473,6 +491,7 @@ static int ip6_frag_xmit(struct sock *sk IP6_INC_STATS(Ip6FragCreates); IP6_INC_STATS(Ip6OutRequests); + ip6_mark_mipv6_packet(opt, skb); err = NF_HOOK(PF_INET6,NF_IP6_LOCAL_OUT, skb, NULL, dst->dev, ip6_maybe_reroute); if (err) { kfree_skb(last_skb); @@ -499,6 +518,7 @@ static int ip6_frag_xmit(struct sock *sk IP6_INC_STATS(Ip6FragCreates); IP6_INC_STATS(Ip6FragOKs); IP6_INC_STATS(Ip6OutRequests); + ip6_mark_mipv6_packet(opt, last_skb); return NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, last_skb, NULL,dst->dev, ip6_maybe_reroute); } @@ -509,26 +529,43 @@ int ip6_build_xmit(struct sock *sk, inet struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6; struct in6_addr *final_dst = NULL; struct dst_entry *dst; + struct rt6_info *rt; int err = 0; unsigned int pktlength, jumbolen, mtu; struct in6_addr saddr; + struct ipv6_txoptions *orig_opt = opt; +#ifdef CONFIG_IPV6_SUBTREES + struct dst_entry *org_dst; +#endif + + opt = ip6_add_mipv6_txoptions(sk, NULL, orig_opt, fl, NULL); + + if(orig_opt && !opt) + return -ENOMEM; if (opt && opt->srcrt) { struct rt0_hdr *rt0 = (struct rt0_hdr *) opt->srcrt; final_dst = fl->fl6_dst; fl->fl6_dst = rt0->addr; - } + } else if (opt && opt->srcrt2) { + struct rt2_hdr *rt2 = (struct rt2_hdr *) opt->srcrt2; + final_dst = fl->fl6_dst; + fl->fl6_dst = &rt2->addr; + } if (!fl->oif && ipv6_addr_is_multicast(fl->nl_u.ip6_u.daddr)) fl->oif = np->mcast_oif; dst = __sk_dst_check(sk, np->dst_cookie); +#ifdef CONFIG_IPV6_SUBTREES + org_dst = dst; +#endif if (dst) { - struct rt6_info *rt = (struct rt6_info*)dst; + rt = (struct rt6_info*)dst; /* Yes, checking route validity in not connected case is not very simple. Take into account, - that we do not support routing by source, TOS, + that we do not support routing by TOS, and MSG_DONTROUTE --ANK (980726) 1. If route was host route, check that @@ -548,6 +585,13 @@ int ip6_build_xmit(struct sock *sk, inet ipv6_addr_cmp(fl->fl6_dst, &rt->rt6i_dst.addr)) && (np->daddr_cache == NULL || ipv6_addr_cmp(fl->fl6_dst, np->daddr_cache))) +#ifdef CONFIG_IPV6_SUBTREES + || (fl->fl6_src != NULL + && (rt->rt6i_src.plen != 128 || + ipv6_addr_cmp(fl->fl6_src, &rt->rt6i_src.addr)) + && (np->saddr_cache == NULL || + ipv6_addr_cmp(fl->fl6_src, np->saddr_cache))) +#endif || (fl->oif && fl->oif != dst->dev->ifindex)) { dst = NULL; } else @@ -560,21 +604,42 @@ int ip6_build_xmit(struct sock *sk, inet if (dst->error) { IP6_INC_STATS(Ip6OutNoRoutes); dst_release(dst); + ip6_free_mipv6_txoptions(opt, orig_opt); return -ENETUNREACH; } if (fl->fl6_src == NULL) { err = ipv6_get_saddr(dst, fl->fl6_dst, &saddr); - if (err) { #if IP6_DEBUG >= 2 printk(KERN_DEBUG "ip6_build_xmit: " "no available source address\n"); #endif + +#ifdef CONFIG_IPV6_SUBTREES + if (dst != org_dst) { + dst_release(dst); + dst = org_dst; + } +#endif goto out; } fl->fl6_src = &saddr; } +#ifdef CONFIG_IPV6_SUBTREES + rt = (struct rt6_info*)dst; + if (dst != org_dst || rt->rt6i_src.plen != 128 || + ipv6_addr_cmp(fl->fl6_src, &rt->rt6i_src.addr)) { + dst_release(dst); + dst = ip6_route_output(sk, fl); + if (dst->error) { + IP6_INC_STATS(Ip6OutNoRoutes); + dst_release(dst); + ip6_free_mipv6_txoptions(opt, orig_opt); + return -ENETUNREACH; + } + } +#endif pktlength = length; if (hlimit < 0) { @@ -667,6 +732,7 @@ int ip6_build_xmit(struct sock *sk, inet if (!err) { IP6_INC_STATS(Ip6OutRequests); + ip6_mark_mipv6_packet(opt, skb); err = NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, dst->dev, ip6_maybe_reroute); } else { err = -EFAULT; @@ -688,9 +754,14 @@ int ip6_build_xmit(struct sock *sk, inet * cleanup */ out: - ip6_dst_store(sk, dst, fl->nl_u.ip6_u.daddr == &np->daddr ? &np->daddr : NULL); + ip6_dst_store(sk, dst, + fl->nl_u.ip6_u.daddr == &np->daddr ? &np->daddr : NULL, + fl->nl_u.ip6_u.saddr == &np->saddr ? &np->saddr : NULL); if (err > 0) err = np->recverr ? net_xmit_errno(err) : 0; + + ip6_free_mipv6_txoptions(opt, orig_opt); + return err; } @@ -769,6 +840,15 @@ int ip6_forward(struct sk_buff *skb) return -ETIMEDOUT; } + /* The proxying router can't forward traffic sent to a link-local + address, so signal the sender and discard the packet. This + behavior is required by the MIPv6 specification. */ + + if (ipv6_addr_type(&hdr->daddr) & IPV6_ADDR_LINKLOCAL && + skb->dev && pneigh_lookup(&nd_tbl, &hdr->daddr, skb->dev, 0)) { + dst_link_failure(skb); + goto drop; + } /* IPv6 specs say nothing about it, but it is clear that we cannot send redirects to source routed frames. */ diff -uprN linux-2.4.25.old/net/ipv6/ipv6_syms.c linux-2.4.25/net/ipv6/ipv6_syms.c --- linux-2.4.25.old/net/ipv6/ipv6_syms.c 2003-11-28 18:26:21.000000000 +0000 +++ linux-2.4.25/net/ipv6/ipv6_syms.c 2004-06-26 11:29:30.000000000 +0100 @@ -6,6 +6,8 @@ #include #include #include +#include +#include EXPORT_SYMBOL(ipv6_addr_type); EXPORT_SYMBOL(icmpv6_send); @@ -33,3 +35,48 @@ EXPORT_SYMBOL(inet6_ioctl); EXPORT_SYMBOL(ipv6_get_saddr); EXPORT_SYMBOL(ipv6_chk_addr); EXPORT_SYMBOL(in6_dev_finish_destroy); + +#if defined(CONFIG_IPV6_TUNNEL_MODULE) || defined(CONFIG_IPV6_MOBILITY_MODULE) +EXPORT_SYMBOL(ip6_build_xmit); +EXPORT_SYMBOL(rt6_lookup); +EXPORT_SYMBOL(ipv6_ext_hdr); +#endif +#ifdef CONFIG_IPV6_MOBILITY_MODULE +EXPORT_SYMBOL(mipv6_functions); +EXPORT_SYMBOL(mipv6_invalidate_calls); +#if defined(CONFIG_IPV6_MOBILITY_HA_MODULE) || defined(CONFIG_IPV6_MOBILITY_MN_MODULE) +EXPORT_SYMBOL(ip6_route_add); +EXPORT_SYMBOL(ip6_route_del); +EXPORT_SYMBOL(ipv6_get_lladdr); +EXPORT_SYMBOL(ipv6_get_ifaddr); +EXPORT_SYMBOL(nd_tbl); +EXPORT_SYMBOL(ndisc_send_ns); +EXPORT_SYMBOL(ndisc_send_na); +EXPORT_SYMBOL(ndisc_next_option); +EXPORT_SYMBOL(inet6_ifa_finish_destroy); +#endif +#ifdef CONFIG_IPV6_MOBILITY_HA_MODULE +EXPORT_SYMBOL(ipv6_dev_ac_dec); +EXPORT_SYMBOL(ipv6_dev_ac_inc); +EXPORT_SYMBOL(ipv6_dev_mc_dec); +EXPORT_SYMBOL(ipv6_dev_mc_inc); +EXPORT_SYMBOL(ip6_forward); +EXPORT_SYMBOL(ip6_input); +EXPORT_SYMBOL(ipv6_chk_acast_addr); +#endif +#ifdef CONFIG_IPV6_MOBILITY_MN_MODULE +#endif +EXPORT_SYMBOL(addrconf_add_ifaddr); +EXPORT_SYMBOL(addrconf_del_ifaddr); +EXPORT_SYMBOL(addrconf_dad_start); +EXPORT_SYMBOL(ip6_del_rt); +EXPORT_SYMBOL(ip6_routing_table); +EXPORT_SYMBOL(rt6_get_dflt_router); +EXPORT_SYMBOL(rt6_purge_dflt_routers); +EXPORT_SYMBOL(rt6_lock); +EXPORT_SYMBOL(ndisc_send_rs); +EXPORT_SYMBOL(fib6_clean_tree); +EXPORT_SYMBOL(ipv6_del_addr); +EXPORT_SYMBOL(ipv6_generate_eui64); +EXPORT_SYMBOL(ipv6_inherit_eui64); +#endif diff -uprN linux-2.4.25.old/net/ipv6/ipv6_tunnel.c linux-2.4.25/net/ipv6/ipv6_tunnel.c --- linux-2.4.25.old/net/ipv6/ipv6_tunnel.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.25/net/ipv6/ipv6_tunnel.c 2004-06-26 11:29:30.000000000 +0100 @@ -0,0 +1,1604 @@ +/* + * IPv6 over IPv6 tunnel device + * Linux INET6 implementation + * + * Authors: + * Ville Nuorvala + * + * $Id$ + * + * Based on: + * linux/net/ipv6/sit.c + * + * RFC 2473 + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Ville Nuorvala"); +MODULE_DESCRIPTION("IPv6-in-IPv6 tunnel"); +MODULE_LICENSE("GPL"); + +#define IPV6_TLV_TEL_DST_SIZE 8 + +#ifdef IP6_TNL_DEBUG +#define IP6_TNL_TRACE(x...) printk(KERN_DEBUG "%s:" x "\n", __FUNCTION__) +#else +#define IP6_TNL_TRACE(x...) do {;} while(0) +#endif + +#define IPV6_TCLASS_MASK (IPV6_FLOWINFO_MASK & ~IPV6_FLOWLABEL_MASK) + +#define HASH_SIZE 32 + +#define HASH(addr) (((addr)->s6_addr32[0] ^ (addr)->s6_addr32[1] ^ \ + (addr)->s6_addr32[2] ^ (addr)->s6_addr32[3]) & \ + (HASH_SIZE - 1)) + +static int ip6ip6_fb_tnl_dev_init(struct net_device *dev); +static int ip6ip6_tnl_dev_init(struct net_device *dev); + +/* the IPv6 IPv6 tunnel fallback device */ +static struct net_device ip6ip6_fb_tnl_dev = { + name: "ip6tnl0", + init: ip6ip6_fb_tnl_dev_init +}; + +/* the IPv6 IPv6 fallback tunnel */ +static struct ip6_tnl ip6ip6_fb_tnl = { + dev: &ip6ip6_fb_tnl_dev, + parms:{name: "ip6tnl0", proto: IPPROTO_IPV6} +}; + +/* lists for storing tunnels in use */ +static struct ip6_tnl *tnls_r_l[HASH_SIZE]; +static struct ip6_tnl *tnls_wc[1]; +static struct ip6_tnl **tnls[2] = { tnls_wc, tnls_r_l }; + +/* list for unused cached kernel tunnels */ +static struct ip6_tnl *tnls_kernel[1]; +/* maximum number of cached kernel tunnels */ +static unsigned int max_kdev_count = 0; +/* minimum number of cached kernel tunnels */ +static unsigned int min_kdev_count = 0; +/* current number of cached kernel tunnels */ +static unsigned int kdev_count = 0; + +/* lists for tunnel hook functions */ +static struct list_head hooks[IP6_TNL_MAXHOOKS]; + +/* locks for the different lists */ +static rwlock_t ip6ip6_lock = RW_LOCK_UNLOCKED; +static rwlock_t ip6ip6_kernel_lock = RW_LOCK_UNLOCKED; +static rwlock_t ip6ip6_hook_lock = RW_LOCK_UNLOCKED; + +/* flag indicating if the module is being removed */ +static int shutdown = 0; + +/** + * ip6ip6_tnl_lookup - fetch tunnel matching the end-point addresses + * @remote: the address of the tunnel exit-point + * @local: the address of the tunnel entry-point + * + * Return: + * tunnel matching given end-points if found, + * else fallback tunnel if its device is up, + * else %NULL + **/ + +struct ip6_tnl * +ip6ip6_tnl_lookup(struct in6_addr *remote, struct in6_addr *local) +{ + unsigned h0 = HASH(remote); + unsigned h1 = HASH(local); + struct ip6_tnl *t; + + for (t = tnls_r_l[h0 ^ h1]; t; t = t->next) { + if (!ipv6_addr_cmp(local, &t->parms.laddr) && + !ipv6_addr_cmp(remote, &t->parms.raddr) && + (t->dev->flags & IFF_UP)) + return t; + } + if ((t = tnls_wc[0]) != NULL && (t->dev->flags & IFF_UP)) + return t; + + return NULL; +} + +/** + * ip6ip6_bucket - get head of list matching given tunnel parameters + * @p: parameters containing tunnel end-points + * + * Description: + * ip6ip6_bucket() returns the head of the list matching the + * &struct in6_addr entries laddr and raddr in @p. + * + * Return: head of IPv6 tunnel list + **/ + +static struct ip6_tnl ** +ip6ip6_bucket(struct ip6_tnl_parm *p) +{ + struct in6_addr *remote = &p->raddr; + struct in6_addr *local = &p->laddr; + unsigned h = 0; + int prio = 0; + + if (!ipv6_addr_any(remote) || !ipv6_addr_any(local)) { + prio = 1; + h = HASH(remote) ^ HASH(local); + } + return &tnls[prio][h]; +} + +/** + * ip6ip6_kernel_tnl_link - add new kernel tunnel to cache + * @t: kernel tunnel + * + * Note: + * %IP6_TNL_F_KERNEL_DEV is assumed to be raised in t->parms.flags. + * See the comments on ip6ip6_kernel_tnl_add() for more information. + **/ + +static inline void +ip6ip6_kernel_tnl_link(struct ip6_tnl *t) +{ + write_lock_bh(&ip6ip6_kernel_lock); + t->next = tnls_kernel[0]; + tnls_kernel[0] = t; + kdev_count++; + write_unlock_bh(&ip6ip6_kernel_lock); +} + +/** + * ip6ip6_kernel_tnl_unlink - remove first kernel tunnel from cache + * + * Return: first free kernel tunnel + * + * Note: + * See the comments on ip6ip6_kernel_tnl_add() for more information. + **/ + +static inline struct ip6_tnl * +ip6ip6_kernel_tnl_unlink(void) +{ + struct ip6_tnl *t; + + write_lock_bh(&ip6ip6_kernel_lock); + if ((t = tnls_kernel[0]) != NULL) { + tnls_kernel[0] = t->next; + kdev_count--; + } + write_unlock_bh(&ip6ip6_kernel_lock); + return t; +} + +/** + * ip6ip6_tnl_link - add tunnel to hash table + * @t: tunnel to be added + **/ + +static void +ip6ip6_tnl_link(struct ip6_tnl *t) +{ + struct ip6_tnl **tp = ip6ip6_bucket(&t->parms); + + write_lock_bh(&ip6ip6_lock); + t->next = *tp; + *tp = t; + write_unlock_bh(&ip6ip6_lock); +} + +/** + * ip6ip6_tnl_unlink - remove tunnel from hash table + * @t: tunnel to be removed + **/ + +static void +ip6ip6_tnl_unlink(struct ip6_tnl *t) +{ + struct ip6_tnl **tp; + + write_lock_bh(&ip6ip6_lock); + for (tp = ip6ip6_bucket(&t->parms); *tp; tp = &(*tp)->next) { + if (t == *tp) { + *tp = t->next; + break; + } + } + write_unlock_bh(&ip6ip6_lock); +} + +/** + * ip6ip6_tnl_create() - create a new tunnel + * @p: tunnel parameters + * @pt: pointer to new tunnel + * + * Description: + * Create tunnel matching given parameters. New kernel managed devices are + * not put in the normal hash structure, but are instead cached for later + * use. + * + * Return: + * 0 on success + **/ + + +static int __ip6ip6_tnl_create(struct ip6_tnl_parm *p, + struct ip6_tnl **pt, + int kernel_list) +{ + struct net_device *dev; + int err = -ENOBUFS; + struct ip6_tnl *t; + + MOD_INC_USE_COUNT; + dev = kmalloc(sizeof (*dev) + sizeof (*t), GFP_KERNEL); + if (!dev) { + MOD_DEC_USE_COUNT; + return err; + } + memset(dev, 0, sizeof (*dev) + sizeof (*t)); + dev->priv = (void *) (dev + 1); + t = (struct ip6_tnl *) dev->priv; + t->dev = dev; + dev->init = ip6ip6_tnl_dev_init; + dev->features |= NETIF_F_DYNALLOC; + if (kernel_list) { + memcpy(t->parms.name, p->name, IFNAMSIZ - 1); + t->parms.proto = IPPROTO_IPV6; + t->parms.flags = IP6_TNL_F_KERNEL_DEV; + } else { + memcpy(&t->parms, p, sizeof (*p)); + } + t->parms.name[IFNAMSIZ - 1] = '\0'; + strcpy(dev->name, t->parms.name); + if (!dev->name[0]) { + int i; + for (i = 0; i < IP6_TNL_MAX; i++) { + sprintf(dev->name, "ip6tnl%d", i); + if (__dev_get_by_name(dev->name) == NULL) + break; + } + + if (i == IP6_TNL_MAX) { + goto failed; + } + memcpy(t->parms.name, dev->name, IFNAMSIZ); + } + if ((err = register_netdevice(dev)) < 0) { + goto failed; + } + dev_hold(dev); + if (kernel_list) { + ip6ip6_kernel_tnl_link(t); + } else { + ip6ip6_tnl_link(t); + } + *pt = t; + return 0; +failed: + kfree(dev); + MOD_DEC_USE_COUNT; + return err; +} + + +int ip6ip6_tnl_create(struct ip6_tnl_parm *p, struct ip6_tnl **pt) +{ + return __ip6ip6_tnl_create(p, pt, 0); +} + + +static void manage_kernel_tnls(void *foo); + +static struct tq_struct manager_task = { + routine:manage_kernel_tnls, + data:NULL +}; + +/** + * manage_kernel_tnls() - create and destroy kernel tunnels + * + * Description: + * manage_kernel_tnls() creates new kernel devices if there + * are less than $min_kdev_count of them and deletes old ones if + * there are less than $max_kdev_count of them in the cache + * + * Note: + * Schedules itself to be run later in process context if called from + * interrupt. Therefore only works synchronously when called from process + * context. + **/ + +static void +manage_kernel_tnls(void *foo) +{ + struct ip6_tnl *t = NULL; + struct ip6_tnl_parm parm; + + /* We can't do this processing in interrupt + context so schedule it for later */ + if (in_interrupt()) { + read_lock(&ip6ip6_kernel_lock); + if (!shutdown && + (kdev_count < min_kdev_count || + kdev_count > max_kdev_count)) { + schedule_task(&manager_task); + } + read_unlock(&ip6ip6_kernel_lock); + return; + } + + rtnl_lock(); + read_lock_bh(&ip6ip6_kernel_lock); + memset(&parm, 0, sizeof (parm)); + parm.flags = IP6_TNL_F_KERNEL_DEV; + /* Create tunnels until there are at least min_kdev_count */ + while (kdev_count < min_kdev_count) { + read_unlock_bh(&ip6ip6_kernel_lock); + if (!__ip6ip6_tnl_create(&parm, &t, 1)) { + dev_open(t->dev); + } else { + goto err; + } + read_lock_bh(&ip6ip6_kernel_lock); + } + + /* Destroy tunnels until there are at most max_kdev_count */ + while (kdev_count > max_kdev_count) { + read_unlock_bh(&ip6ip6_kernel_lock); + if ((t = ip6ip6_kernel_tnl_unlink()) != NULL) { + unregister_netdevice(t->dev); + } else { + goto err; + } + read_lock_bh(&ip6ip6_kernel_lock); + } + read_unlock_bh(&ip6ip6_kernel_lock); +err: + rtnl_unlock(); +} + +/** + * ip6ip6_tnl_inc_max_kdev_count() - increase max kernel dev cache size + * @n: size increase + * Description: + * Increase the upper limit for the number of kernel devices allowed in the + * cache at any on time. + **/ + +unsigned int +ip6ip6_tnl_inc_max_kdev_count(unsigned int n) +{ + write_lock_bh(&ip6ip6_kernel_lock); + max_kdev_count += n; + write_unlock_bh(&ip6ip6_kernel_lock); + manage_kernel_tnls(NULL); + return max_kdev_count; +} + +/** + * ip6ip6_tnl_dec_max_kdev_count() - decrease max kernel dev cache size + * @n: size decrement + * Description: + * Decrease the upper limit for the number of kernel devices allowed in the + * cache at any on time. + **/ + +unsigned int +ip6ip6_tnl_dec_max_kdev_count(unsigned int n) +{ + write_lock_bh(&ip6ip6_kernel_lock); + max_kdev_count -= min(max_kdev_count, n); + if (max_kdev_count < min_kdev_count) + min_kdev_count = max_kdev_count; + write_unlock_bh(&ip6ip6_kernel_lock); + manage_kernel_tnls(NULL); + return max_kdev_count; +} + +/** + * ip6ip6_tnl_inc_min_kdev_count() - increase min kernel dev cache size + * @n: size increase + * Description: + * Increase the lower limit for the number of kernel devices allowed in the + * cache at any on time. + **/ + +unsigned int +ip6ip6_tnl_inc_min_kdev_count(unsigned int n) +{ + write_lock_bh(&ip6ip6_kernel_lock); + min_kdev_count += n; + if (min_kdev_count > max_kdev_count) + max_kdev_count = min_kdev_count; + write_unlock_bh(&ip6ip6_kernel_lock); + manage_kernel_tnls(NULL); + return min_kdev_count; +} + +/** + * ip6ip6_tnl_dec_min_kdev_count() - decrease min kernel dev cache size + * @n: size decrement + * Description: + * Decrease the lower limit for the number of kernel devices allowed in the + * cache at any on time. + **/ + +unsigned int +ip6ip6_tnl_dec_min_kdev_count(unsigned int n) +{ + write_lock_bh(&ip6ip6_kernel_lock); + min_kdev_count -= min(min_kdev_count, n); + write_unlock_bh(&ip6ip6_kernel_lock); + manage_kernel_tnls(NULL); + return min_kdev_count; +} + +/** + * ip6ip6_tnl_locate - find or create tunnel matching given parameters + * @p: tunnel parameters + * @create: != 0 if allowed to create new tunnel if no match found + * + * Description: + * ip6ip6_tnl_locate() first tries to locate an existing tunnel + * based on @parms. If this is unsuccessful, but @create is set a new + * tunnel device is created and registered for use. + * + * Return: + * 0 if tunnel located or created, + * -EINVAL if parameters incorrect, + * -ENODEV if no matching tunnel available + **/ + +int ip6ip6_tnl_locate(struct ip6_tnl_parm *p, struct ip6_tnl **pt, int create) +{ + struct in6_addr *remote = &p->raddr; + struct in6_addr *local = &p->laddr; + struct ip6_tnl *t; + + if (p->proto != IPPROTO_IPV6) + return -EINVAL; + + for (t = *ip6ip6_bucket(p); t; t = t->next) { + if (!ipv6_addr_cmp(local, &t->parms.laddr) && + !ipv6_addr_cmp(remote, &t->parms.raddr)) { + *pt = t; + return (create ? -EEXIST : 0); + } + } + return ip6ip6_tnl_create(p, pt); +} + +/** + * ip6ip6_tnl_dev_destructor - tunnel device destructor + * @dev: the device to be destroyed + **/ + +static void +ip6ip6_tnl_dev_destructor(struct net_device *dev) +{ + if (dev != &ip6ip6_fb_tnl_dev) { + MOD_DEC_USE_COUNT; + } +} + +/** + * ip6ip6_tnl_dev_uninit - tunnel device uninitializer + * @dev: the device to be destroyed + * + * Description: + * ip6ip6_tnl_dev_uninit() removes tunnel from its list + **/ + +static void +ip6ip6_tnl_dev_uninit(struct net_device *dev) +{ + struct ip6_tnl *t = (struct ip6_tnl *) dev->priv; + + if (dev == &ip6ip6_fb_tnl_dev) { + write_lock_bh(&ip6ip6_lock); + tnls_wc[0] = NULL; + write_unlock_bh(&ip6ip6_lock); + } else { + ip6ip6_tnl_unlink(t); + } + sock_release(t->sock); + dev_put(dev); +} + +/** + * parse_tvl_tnl_enc_lim - handle encapsulation limit option + * @skb: received socket buffer + * + * Return: + * 0 if none was found, + * else index to encapsulation limit + **/ + +static __u16 +parse_tlv_tnl_enc_lim(struct sk_buff *skb, __u8 * raw) +{ + struct ipv6hdr *ipv6h = (struct ipv6hdr *) raw; + __u8 nexthdr = ipv6h->nexthdr; + __u16 off = sizeof (*ipv6h); + + while (ipv6_ext_hdr(nexthdr) && nexthdr != NEXTHDR_NONE) { + __u16 optlen = 0; + struct ipv6_opt_hdr *hdr; + if (raw + off + sizeof (*hdr) > skb->data && + !pskb_may_pull(skb, raw - skb->data + off + sizeof (*hdr))) + break; + + hdr = (struct ipv6_opt_hdr *) (raw + off); + if (nexthdr == NEXTHDR_FRAGMENT) { + struct frag_hdr *frag_hdr = (struct frag_hdr *) hdr; + if (frag_hdr->frag_off) + break; + optlen = 8; + } else if (nexthdr == NEXTHDR_AUTH) { + optlen = (hdr->hdrlen + 2) << 2; + } else { + optlen = ipv6_optlen(hdr); + } + if (nexthdr == NEXTHDR_DEST) { + __u16 i = off + 2; + while (1) { + struct ipv6_tlv_tnl_enc_lim *tel; + + /* No more room for encapsulation limit */ + if (i + sizeof (*tel) > off + optlen) + break; + + tel = (struct ipv6_tlv_tnl_enc_lim *) &raw[i]; + /* return index of option if found and valid */ + if (tel->type == IPV6_TLV_TNL_ENCAP_LIMIT && + tel->length == 1) + return i; + /* else jump to next option */ + if (tel->type) + i += tel->length + 2; + else + i++; + } + } + nexthdr = hdr->nexthdr; + off += optlen; + } + return 0; +} + +/** + * ip6ip6_err - tunnel error handler + * + * Description: + * ip6ip6_err() should handle errors in the tunnel according + * to the specifications in RFC 2473. + **/ + +void ip6ip6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, + int type, int code, int offset, __u32 info) +{ + struct ipv6hdr *ipv6h = (struct ipv6hdr *) skb->data; + struct ip6_tnl *t; + int rel_msg = 0; + int rel_type = ICMPV6_DEST_UNREACH; + int rel_code = ICMPV6_ADDR_UNREACH; + __u32 rel_info = 0; + __u16 len; + + /* If the packet doesn't contain the original IPv6 header we are + in trouble since we might need the source address for furter + processing of the error. */ + + read_lock(&ip6ip6_lock); + if ((t = ip6ip6_tnl_lookup(&ipv6h->daddr, &ipv6h->saddr)) == NULL) + goto out; + + switch (type) { + __u32 teli; + struct ipv6_tlv_tnl_enc_lim *tel; + __u32 mtu; + case ICMPV6_DEST_UNREACH: + if (net_ratelimit()) + printk(KERN_WARNING + "%s: Path to destination invalid " + "or inactive!\n", t->parms.name); + rel_msg = 1; + break; + case ICMPV6_TIME_EXCEED: + if (code == ICMPV6_EXC_HOPLIMIT) { + if (net_ratelimit()) + printk(KERN_WARNING + "%s: Too small hop limit or " + "routing loop in tunnel!\n", + t->parms.name); + rel_msg = 1; + } + break; + case ICMPV6_PARAMPROB: + /* ignore if parameter problem not caused by a tunnel + encapsulation limit sub-option */ + if (code != ICMPV6_HDR_FIELD) { + break; + } + teli = parse_tlv_tnl_enc_lim(skb, skb->data); + + if (teli && teli == ntohl(info) - 2) { + tel = (struct ipv6_tlv_tnl_enc_lim *) &skb->data[teli]; + if (tel->encap_limit == 0) { + if (net_ratelimit()) + printk(KERN_WARNING + "%s: Too small encapsulation " + "limit or routing loop in " + "tunnel!\n", t->parms.name); + rel_msg = 1; + } + } + break; + case ICMPV6_PKT_TOOBIG: + mtu = ntohl(info) - offset; + if (mtu < IPV6_MIN_MTU) + mtu = IPV6_MIN_MTU; + t->dev->mtu = mtu; + + if ((len = sizeof (*ipv6h) + ipv6h->payload_len) > mtu) { + rel_type = ICMPV6_PKT_TOOBIG; + rel_code = 0; + rel_info = mtu; + rel_msg = 1; + } + break; + } + if (rel_msg && pskb_may_pull(skb, offset + sizeof (*ipv6h))) { + struct rt6_info *rt; + struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC); + if (!skb2) + goto out; + + dst_release(skb2->dst); + skb2->dst = NULL; + skb_pull(skb2, offset); + skb2->nh.raw = skb2->data; + + /* Try to guess incoming interface */ + rt = rt6_lookup(&skb2->nh.ipv6h->saddr, NULL, 0, 0); + + if (rt && rt->rt6i_dev) + skb2->dev = rt->rt6i_dev; + + icmpv6_send(skb2, rel_type, rel_code, rel_info, skb2->dev); + + if (rt) + dst_release(&rt->u.dst); + + kfree_skb(skb2); + } +out: + read_unlock(&ip6ip6_lock); +} + +/** + * call_hooks - call ipv6 tunnel hooks + * @hooknum: hook number, either %IP6_TNL_PRE_ENCAP, or + * %IP6_TNL_PRE_DECAP + * @t: the current tunnel + * @skb: the tunneled packet + * + * Description: + * Pass packet to all the hook functions until %IP6_TNL_DROP + * + * Return: + * %IP6_TNL_ACCEPT or %IP6_TNL_DROP + **/ + +static inline int +call_hooks(unsigned int hooknum, struct ip6_tnl *t, struct sk_buff *skb) +{ + struct ip6_tnl_hook_ops *h; + int accept = IP6_TNL_ACCEPT; + + if (hooknum < IP6_TNL_MAXHOOKS) { + struct list_head *i; + read_lock(&ip6ip6_hook_lock); + for (i = hooks[hooknum].next; i != &hooks[hooknum]; i = i->next) { + h = (struct ip6_tnl_hook_ops *) i; + + if (h->hook) { + accept = h->hook(t, skb); + + if (accept != IP6_TNL_ACCEPT) + break; + } + } + read_unlock(&ip6ip6_hook_lock); + } + return accept; +} + +/** + * ip6ip6_rcv - decapsulate IPv6 packet and retransmit it locally + * @skb: received socket buffer + * + * Return: 0 + **/ + +int ip6ip6_rcv(struct sk_buff *skb) +{ + struct ipv6hdr *ipv6h; + struct ip6_tnl *t; + + if (!pskb_may_pull(skb, sizeof (*ipv6h))) + goto discard; + + ipv6h = skb->nh.ipv6h; + + read_lock(&ip6ip6_lock); + + if ((t = ip6ip6_tnl_lookup(&ipv6h->saddr, &ipv6h->daddr)) != NULL) { + if (!(t->parms.flags & IP6_TNL_F_CAP_RCV) || + call_hooks(IP6_TNL_PRE_DECAP, t, skb) != IP6_TNL_ACCEPT) { + t->stat.rx_dropped++; + read_unlock(&ip6ip6_lock); + goto discard; + } + skb->mac.raw = skb->nh.raw; + skb->nh.raw = skb->data; + skb->protocol = htons(ETH_P_IPV6); + skb->pkt_type = PACKET_HOST; + memset(skb->cb, 0, sizeof(struct inet6_skb_parm)); + skb->dev = t->dev; + dst_release(skb->dst); + skb->dst = NULL; + t->stat.rx_packets++; + t->stat.rx_bytes += skb->len; + netif_rx(skb); + read_unlock(&ip6ip6_lock); + return 0; + } + read_unlock(&ip6ip6_lock); + icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0, skb->dev); +discard: + kfree_skb(skb); + return 0; +} + +static inline struct ipv6_txoptions *create_tel(__u8 encap_limit) +{ + struct ipv6_tlv_tnl_enc_lim *tel; + struct ipv6_txoptions *opt; + __u8 *raw; + + int opt_len = sizeof(*opt) + IPV6_TLV_TEL_DST_SIZE; + + if (!(opt = kmalloc(opt_len, GFP_ATOMIC))) { + return NULL; + } + memset(opt, 0, opt_len); + opt->tot_len = opt_len; + opt->dst0opt = (struct ipv6_opt_hdr *) (opt + 1); + opt->opt_nflen = 8; + + tel = (struct ipv6_tlv_tnl_enc_lim *) (opt->dst0opt + 1); + tel->type = IPV6_TLV_TNL_ENCAP_LIMIT; + tel->length = 1; + tel->encap_limit = encap_limit; + + raw = (__u8 *) opt->dst0opt; + raw[5] = IPV6_TLV_PADN; + raw[6] = 1; + + return opt; +} + +static int +ip6ip6_getfrag(const void *data, struct in6_addr *addr, + char *buff, unsigned int offset, unsigned int len) +{ + memcpy(buff, data + offset, len); + return 0; +} + +/** + * ip6ip6_tnl_addr_conflict - compare packet addresses to tunnel's own + * @t: the outgoing tunnel device + * @hdr: IPv6 header from the incoming packet + * + * Description: + * Avoid trivial tunneling loop by checking that tunnel exit-point + * doesn't match source of incoming packet. + * + * Return: + * 1 if conflict, + * 0 else + **/ + +static inline int +ip6ip6_tnl_addr_conflict(struct ip6_tnl *t, struct ipv6hdr *hdr) +{ + return !ipv6_addr_cmp(&t->parms.raddr, &hdr->saddr); +} + +/** + * ip6ip6_tnl_xmit - encapsulate packet and send + * @skb: the outgoing socket buffer + * @dev: the outgoing tunnel device + * + * Description: + * Build new header and do some sanity checks on the packet before sending + * it to ip6_build_xmit(). + * + * Return: + * 0 + **/ + +int ip6ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct ip6_tnl *t = (struct ip6_tnl *) dev->priv; + struct net_device_stats *stats = &t->stat; + struct ipv6hdr *ipv6h = skb->nh.ipv6h; + struct ipv6_txoptions *opt = NULL; + int encap_limit = -1; + __u16 offset; + struct flowi fl; + int err = 0; + struct dst_entry *dst; + struct sock *sk = t->sock->sk; + struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6; + int mtu; + + if (t->recursion++) { + stats->collisions++; + goto tx_err; + } + if (skb->protocol != htons(ETH_P_IPV6) || + !(t->parms.flags & IP6_TNL_F_CAP_XMIT) || + ip6ip6_tnl_addr_conflict(t, ipv6h)) { + goto tx_err; + } + if ((offset = parse_tlv_tnl_enc_lim(skb, skb->nh.raw)) > 0) { + struct ipv6_tlv_tnl_enc_lim *tel; + tel = (struct ipv6_tlv_tnl_enc_lim *) &skb->nh.raw[offset]; + if (tel->encap_limit == 0) { + icmpv6_send(skb, ICMPV6_PARAMPROB, + ICMPV6_HDR_FIELD, offset + 2, skb->dev); + goto tx_err; + } + encap_limit = tel->encap_limit - 1; + } else if (!(t->parms.flags & IP6_TNL_F_IGN_ENCAP_LIMIT)) { + encap_limit = t->parms.encap_limit; + } + if (call_hooks(IP6_TNL_PRE_ENCAP, t, skb) != IP6_TNL_ACCEPT) + goto discard; + memcpy(&fl, &t->fl, sizeof (fl)); + + if ((t->parms.flags & IP6_TNL_F_USE_ORIG_TCLASS)) + fl.fl6_flowlabel |= (*(__u32 *) ipv6h & IPV6_TCLASS_MASK); + if ((t->parms.flags & IP6_TNL_F_USE_ORIG_FLOWLABEL)) + fl.fl6_flowlabel |= (*(__u32 *) ipv6h & IPV6_FLOWLABEL_MASK); + + if (encap_limit >= 0 && (opt = create_tel(encap_limit)) == NULL) + goto tx_err; + + dst = __sk_dst_check(sk, np->dst_cookie); + + if (dst) { + if (np->daddr_cache == NULL || + ipv6_addr_cmp(fl.fl6_dst, np->daddr_cache) || +#ifdef CONFIG_IPV6_SUBTREES + np->saddr_cache == NULL || + ipv6_addr_cmp(fl.fl6_src, np->saddr_cache) || +#endif + (fl.oif && fl.oif != dst->dev->ifindex)) { + dst = NULL; + } else { + dst_hold(dst); + } + } + if (dst == NULL) { + dst = ip6_route_output(sk, &fl); + if (dst->error) { + stats->tx_carrier_errors++; + dst_link_failure(skb); + goto tx_err_dst_release; + } + /* local routing loop */ + if (dst->dev == dev) { + stats->collisions++; + if (net_ratelimit()) + printk(KERN_WARNING + "%s: Local routing loop detected!\n", + t->parms.name); + goto tx_err_dst_release; + } + } + mtu = dst->pmtu - sizeof (*ipv6h); + if (opt) { + mtu -= (opt->opt_nflen + opt->opt_flen); + } + if (mtu < IPV6_MIN_MTU) + mtu = IPV6_MIN_MTU; + if (skb->dst && mtu < skb->dst->pmtu) { + struct rt6_info *rt = (struct rt6_info *) skb->dst; + rt->rt6i_flags |= RTF_MODIFIED; + rt->u.dst.pmtu = mtu; + } + if (skb->len > mtu) { + icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu, dev); + goto tx_err_dst_release; + } + ip6_dst_store(sk, dst, &np->daddr, &np->saddr); + err = ip6_build_xmit(sk, ip6ip6_getfrag, (void *) skb->nh.raw, + &fl, skb->len, opt, t->parms.hop_limit, + MSG_DONTWAIT); + + if (err == NET_XMIT_SUCCESS || err == NET_XMIT_CN) { + stats->tx_bytes += skb->len; + stats->tx_packets++; + } else { + stats->tx_errors++; + stats->tx_aborted_errors++; + } + if (opt) + kfree(opt); + kfree_skb(skb); + t->recursion--; + return 0; +tx_err_dst_release: + dst_release(dst); + if (opt) + kfree(opt); +tx_err: + stats->tx_errors++; +discard: + stats->tx_dropped++; + kfree_skb(skb); + t->recursion--; + return 0; +} + +static void ip6_tnl_set_cap(struct ip6_tnl *t) +{ + struct ip6_tnl_parm *p = &t->parms; + struct in6_addr *laddr = &p->laddr; + struct in6_addr *raddr = &p->raddr; + int ltype = ipv6_addr_type(laddr); + int rtype = ipv6_addr_type(raddr); + + p->flags &= ~(IP6_TNL_F_CAP_XMIT|IP6_TNL_F_CAP_RCV); + + if (ltype != IPV6_ADDR_ANY && rtype != IPV6_ADDR_ANY && + ((ltype|rtype) & + (IPV6_ADDR_UNICAST| + IPV6_ADDR_LOOPBACK|IPV6_ADDR_LINKLOCAL| + IPV6_ADDR_MAPPED|IPV6_ADDR_RESERVED)) == IPV6_ADDR_UNICAST) { + struct net_device *ldev = NULL; + int l_ok = 1; + int r_ok = 1; + + if (p->link) + ldev = dev_get_by_index(p->link); + + if ((ltype&IPV6_ADDR_UNICAST) && !ipv6_chk_addr(laddr, ldev)) + l_ok = 0; + + if ((rtype&IPV6_ADDR_UNICAST) && ipv6_chk_addr(raddr, NULL)) + r_ok = 0; + + if (l_ok && r_ok) { + if (ltype&IPV6_ADDR_UNICAST) + p->flags |= IP6_TNL_F_CAP_XMIT; + if (rtype&IPV6_ADDR_UNICAST) + p->flags |= IP6_TNL_F_CAP_RCV; + } + if (ldev) + dev_put(ldev); + } +} + +static void ip6ip6_tnl_link_config(struct ip6_tnl *t) +{ + struct net_device *dev = t->dev; + struct ip6_tnl_parm *p = &t->parms; + struct flowi *fl = &t->fl; + + /* Set up flowi template */ + fl->fl6_src = &p->laddr; + fl->fl6_dst = &p->raddr; + fl->oif = p->link; + fl->fl6_flowlabel = 0; + + if (!(p->flags&IP6_TNL_F_USE_ORIG_TCLASS)) + fl->fl6_flowlabel |= IPV6_TCLASS_MASK & htonl(p->flowinfo); + if (!(p->flags&IP6_TNL_F_USE_ORIG_FLOWLABEL)) + fl->fl6_flowlabel |= IPV6_FLOWLABEL_MASK & htonl(p->flowinfo); + + ip6_tnl_set_cap(t); + + if (p->flags&IP6_TNL_F_CAP_XMIT && p->flags&IP6_TNL_F_CAP_RCV) + dev->flags |= IFF_POINTOPOINT; + else + dev->flags &= ~IFF_POINTOPOINT; + + if (p->flags & IP6_TNL_F_CAP_XMIT) { + struct rt6_info *rt = rt6_lookup(&p->raddr, &p->laddr, + p->link, 0); + + if (rt == NULL) + return; + + if (rt->rt6i_dev) { + dev->iflink = rt->rt6i_dev->ifindex; + + dev->hard_header_len = rt->rt6i_dev->hard_header_len + + sizeof (struct ipv6hdr); + + dev->mtu = rt->rt6i_dev->mtu - sizeof (struct ipv6hdr); + + if (dev->mtu < IPV6_MIN_MTU) + dev->mtu = IPV6_MIN_MTU; + } + dst_release(&rt->u.dst); + } +} + +/** + * __ip6ip6_tnl_change - update the tunnel parameters + * @t: tunnel to be changed + * @p: tunnel configuration parameters + * + * Description: + * __ip6ip6_tnl_change() updates the tunnel parameters + **/ + +static void +__ip6ip6_tnl_change(struct ip6_tnl *t, struct ip6_tnl_parm *p) +{ + ipv6_addr_copy(&t->parms.laddr, &p->laddr); + ipv6_addr_copy(&t->parms.raddr, &p->raddr); + t->parms.flags = p->flags; + t->parms.hop_limit = p->hop_limit; + t->parms.encap_limit = p->encap_limit; + t->parms.flowinfo = p->flowinfo; + ip6ip6_tnl_link_config(t); +} + +void ip6ip6_tnl_change(struct ip6_tnl *t, struct ip6_tnl_parm *p) +{ + ip6ip6_tnl_unlink(t); + __ip6ip6_tnl_change(t, p); + ip6ip6_tnl_link(t); +} + +/** + * ip6ip6_kernel_tnl_add - configure and add kernel tunnel to hash + * @p: kernel tunnel configuration parameters + * + * Description: + * ip6ip6_kernel_tnl_add() fetches an unused kernel tunnel configures + * it according to @p and places it among the active tunnels. + * + * Return: + * number of references to tunnel on success, + * %-EEXIST if there is already a device matching description + * %-EINVAL if p->flags doesn't have %IP6_TNL_F_KERNEL_DEV raised, + * %-ENODEV if there are no unused kernel tunnels available + * + * Note: + * The code for creating, opening, closing and destroying network devices + * must be called from process context, while the Mobile IP code, which + * needs the tunnel devices, unfortunately runs in interrupt context. + * + * The devices must be created and opened in advance, then placed in a + * list where the kernel can fetch and ready them for use at a later time. + * + **/ + +int +ip6ip6_kernel_tnl_add(struct ip6_tnl_parm *p) +{ + struct ip6_tnl *t; + + if (!(p->flags & IP6_TNL_F_KERNEL_DEV)) + return -EINVAL; + if ((t = ip6ip6_tnl_lookup(&p->raddr, &p->laddr)) != NULL && + t != &ip6ip6_fb_tnl) { + /* Handle duplicate tunnels by incrementing + reference count */ + atomic_inc(&t->refcnt); + goto out; + } + if ((t = ip6ip6_kernel_tnl_unlink()) == NULL) + return -ENODEV; + __ip6ip6_tnl_change(t, p); + + atomic_inc(&t->refcnt); + + ip6ip6_tnl_link(t); + + manage_kernel_tnls(NULL); +out: + return atomic_read(&t->refcnt); +} + +/** + * ip6ip6_kernel_tnl_del - delete no longer needed kernel tunnel + * @t: kernel tunnel to be removed from hash + * + * Description: + * ip6ip6_kernel_tnl_del() removes and deconfigures the tunnel @t + * and places it among the unused kernel devices. + * + * Return: + * number of references on success, + * %-EINVAL if p->flags doesn't have %IP6_TNL_F_KERNEL_DEV raised, + * + * Note: + * See the comments on ip6ip6_kernel_tnl_add() for more information. + **/ + +int +ip6ip6_kernel_tnl_del(struct ip6_tnl *t) +{ + if (!t) + return -ENODEV; + + if (!(t->parms.flags & IP6_TNL_F_KERNEL_DEV)) + return -EINVAL; + + if (atomic_dec_and_test(&t->refcnt)) { + struct ip6_tnl_parm p; + ip6ip6_tnl_unlink(t); + memset(&p, 0, sizeof (p)); + p.flags = IP6_TNL_F_KERNEL_DEV; + + __ip6ip6_tnl_change(t, &p); + + ip6ip6_kernel_tnl_link(t); + + manage_kernel_tnls(NULL); + } + return atomic_read(&t->refcnt); +} + +/** + * ip6ip6_tnl_ioctl - configure ipv6 tunnels from userspace + * @dev: virtual device associated with tunnel + * @ifr: parameters passed from userspace + * @cmd: command to be performed + * + * Description: + * ip6ip6_tnl_ioctl() is used for managing IPv6 tunnels + * from userspace. + * + * The possible commands are the following: + * %SIOCGETTUNNEL: get tunnel parameters for device + * %SIOCADDTUNNEL: add tunnel matching given tunnel parameters + * %SIOCCHGTUNNEL: change tunnel parameters to those given + * %SIOCDELTUNNEL: delete tunnel + * + * The fallback device "ip6tnl0", created during module + * initialization, can be used for creating other tunnel devices. + * + * Return: + * 0 on success, + * %-EFAULT if unable to copy data to or from userspace, + * %-EPERM if current process hasn't %CAP_NET_ADMIN set or attempting + * to configure kernel devices from userspace, + * %-EINVAL if passed tunnel parameters are invalid, + * %-EEXIST if changing a tunnel's parameters would cause a conflict + * %-ENODEV if attempting to change or delete a nonexisting device + * + * Note: + * See the comments on ip6ip6_kernel_tnl_add() for more information + * about kernel tunnels. + * **/ + +static int +ip6ip6_tnl_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +{ + int err = 0; + int create; + struct ip6_tnl_parm p; + struct ip6_tnl *t = NULL; + + MOD_INC_USE_COUNT; + + switch (cmd) { + case SIOCGETTUNNEL: + if (dev == &ip6ip6_fb_tnl_dev) { + if (copy_from_user(&p, + ifr->ifr_ifru.ifru_data, + sizeof (p))) { + err = -EFAULT; + break; + } + if ((err = ip6ip6_tnl_locate(&p, &t, 0)) == -ENODEV) + t = (struct ip6_tnl *) dev->priv; + else if (err) + break; + } else + t = (struct ip6_tnl *) dev->priv; + + memcpy(&p, &t->parms, sizeof (p)); + if (copy_to_user(ifr->ifr_ifru.ifru_data, &p, sizeof (p))) { + err = -EFAULT; + } + break; + case SIOCADDTUNNEL: + case SIOCCHGTUNNEL: + err = -EPERM; + create = (cmd == SIOCADDTUNNEL); + if (!capable(CAP_NET_ADMIN)) + break; + if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof (p))) { + err = -EFAULT; + break; + } + if (p.flags & IP6_TNL_F_KERNEL_DEV) { + break; + } + if (!create && dev != &ip6ip6_fb_tnl_dev) { + t = (struct ip6_tnl *) dev->priv; + } + if (!t && (err = ip6ip6_tnl_locate(&p, &t, create))) { + break; + } + if (cmd == SIOCCHGTUNNEL) { + if (t->dev != dev) { + err = -EEXIST; + break; + } + if (t->parms.flags & IP6_TNL_F_KERNEL_DEV) { + err = -EPERM; + break; + } + ip6ip6_tnl_change(t, &p); + netdev_state_change(dev); + } + if (copy_to_user(ifr->ifr_ifru.ifru_data, + &t->parms, sizeof (p))) { + err = -EFAULT; + } else { + err = 0; + } + break; + case SIOCDELTUNNEL: + err = -EPERM; + if (!capable(CAP_NET_ADMIN)) + break; + + if (dev == &ip6ip6_fb_tnl_dev) { + if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, + sizeof (p))) { + err = -EFAULT; + break; + } + err = ip6ip6_tnl_locate(&p, &t, 0); + if (err) + break; + if (t == &ip6ip6_fb_tnl) { + err = -EPERM; + break; + } + } else { + t = (struct ip6_tnl *) dev->priv; + } + if (t->parms.flags & IP6_TNL_F_KERNEL_DEV) + err = -EPERM; + else + err = unregister_netdevice(t->dev); + break; + default: + err = -EINVAL; + } + MOD_DEC_USE_COUNT; + return err; +} + +/** + * ip6ip6_tnl_get_stats - return the stats for tunnel device + * @dev: virtual device associated with tunnel + * + * Return: stats for device + **/ + +static struct net_device_stats * +ip6ip6_tnl_get_stats(struct net_device *dev) +{ + return &(((struct ip6_tnl *) dev->priv)->stat); +} + +/** + * ip6ip6_tnl_change_mtu - change mtu manually for tunnel device + * @dev: virtual device associated with tunnel + * @new_mtu: the new mtu + * + * Return: + * 0 on success, + * %-EINVAL if mtu too small + **/ + +static int +ip6ip6_tnl_change_mtu(struct net_device *dev, int new_mtu) +{ + if (new_mtu < IPV6_MIN_MTU) { + return -EINVAL; + } + dev->mtu = new_mtu; + return 0; +} + +/** + * ip6ip6_tnl_dev_init_gen - general initializer for all tunnel devices + * @dev: virtual device associated with tunnel + * + * Description: + * Set function pointers and initialize the &struct flowi template used + * by the tunnel. + **/ + +static int +ip6ip6_tnl_dev_init_gen(struct net_device *dev) +{ + struct ip6_tnl *t = (struct ip6_tnl *) dev->priv; + struct flowi *fl = &t->fl; + int err; + struct sock *sk; + + if ((err = sock_create(PF_INET6, SOCK_RAW, IPPROTO_IPV6, &t->sock))) { + printk(KERN_ERR + "Failed to create IPv6 tunnel socket (err %d).\n", err); + return err; + } + t->sock->inode->i_uid = 0; + t->sock->inode->i_gid = 0; + + sk = t->sock->sk; + sk->allocation = GFP_ATOMIC; + sk->net_pinfo.af_inet6.hop_limit = 254; + sk->net_pinfo.af_inet6.mc_loop = 0; + sk->prot->unhash(sk); + + memset(fl, 0, sizeof (*fl)); + fl->proto = IPPROTO_IPV6; + + dev->destructor = ip6ip6_tnl_dev_destructor; + dev->uninit = ip6ip6_tnl_dev_uninit; + dev->hard_start_xmit = ip6ip6_tnl_xmit; + dev->get_stats = ip6ip6_tnl_get_stats; + dev->do_ioctl = ip6ip6_tnl_ioctl; + dev->change_mtu = ip6ip6_tnl_change_mtu; + + dev->type = ARPHRD_TUNNEL6; + dev->hard_header_len = LL_MAX_HEADER + sizeof (struct ipv6hdr); + dev->mtu = ETH_DATA_LEN - sizeof (struct ipv6hdr); + dev->flags |= IFF_NOARP; + dev->iflink = 0; + /* Hmm... MAX_ADDR_LEN is 8, so the ipv6 addresses can't be + copied to dev->dev_addr and dev->broadcast, like the ipv4 + addresses were in ipip.c, ip_gre.c and sit.c. */ + dev->addr_len = 0; + return 0; +} + +/** + * ip6ip6_tnl_dev_init - initializer for all non fallback tunnel devices + * @dev: virtual device associated with tunnel + **/ + +static int +ip6ip6_tnl_dev_init(struct net_device *dev) +{ + struct ip6_tnl *t = (struct ip6_tnl *) dev->priv; + ip6ip6_tnl_dev_init_gen(dev); + ip6ip6_tnl_link_config(t); + return 0; +} + +#ifdef MODULE + +/** + * ip6ip6_fb_tnl_open - function called when fallback device opened + * @dev: fallback device + * + * Return: 0 + **/ + +static int +ip6ip6_fb_tnl_open(struct net_device *dev) +{ + MOD_INC_USE_COUNT; + return 0; +} + +/** + * ip6ip6_fb_tnl_close - function called when fallback device closed + * @dev: fallback device + * + * Return: 0 + **/ + +static int +ip6ip6_fb_tnl_close(struct net_device *dev) +{ + MOD_DEC_USE_COUNT; + return 0; +} +#endif + +/** + * ip6ip6_fb_tnl_dev_init - initializer for fallback tunnel device + * @dev: fallback device + * + * Return: 0 + **/ + +int __init +ip6ip6_fb_tnl_dev_init(struct net_device *dev) +{ + ip6ip6_tnl_dev_init_gen(dev); +#ifdef MODULE + dev->open = ip6ip6_fb_tnl_open; + dev->stop = ip6ip6_fb_tnl_close; +#endif + dev_hold(dev); + tnls_wc[0] = &ip6ip6_fb_tnl; + return 0; +} + +/** + * ip6ip6_tnl_register_hook - add hook for processing of tunneled packets + * @reg: hook function and its parameters + * + * Description: + * Add a netfilter like hook function for special handling of tunneled + * packets. The hook functions are called before encapsulation + * (%IP6_TNL_PRE_ENCAP) and before decapsulation + * (%IP6_TNL_PRE_DECAP). The possible return values by the hook + * functions are %IP6_TNL_DROP, %IP6_TNL_ACCEPT and + * %IP6_TNL_STOLEN (in case the hook function took care of the packet + * and it doesn't have to be processed any further). + **/ + +void +ip6ip6_tnl_register_hook(struct ip6_tnl_hook_ops *reg) +{ + if (reg->hooknum < IP6_TNL_MAXHOOKS) { + struct list_head *i; + + write_lock_bh(&ip6ip6_hook_lock); + for (i = hooks[reg->hooknum].next; + i != &hooks[reg->hooknum]; i = i->next) { + if (reg->priority < + ((struct ip6_tnl_hook_ops *) i)->priority) { + break; + } + } + list_add(®->list, i->prev); + write_unlock_bh(&ip6ip6_hook_lock); + } +} + +/** + * ip6ip6_tnl_unregister_hook - remove tunnel hook + * @reg: hook function and its parameters + **/ + +void +ip6ip6_tnl_unregister_hook(struct ip6_tnl_hook_ops *reg) +{ + if (reg->hooknum < IP6_TNL_MAXHOOKS) { + write_lock_bh(&ip6ip6_hook_lock); + list_del(®->list); + write_unlock_bh(&ip6ip6_hook_lock); + } +} + + +/* the IPv6 over IPv6 protocol structure */ +static struct inet6_protocol ip6ip6_protocol = { + ip6ip6_rcv, /* IPv6 handler */ + ip6ip6_err, /* IPv6 error control */ + NULL, /* next */ + IPPROTO_IPV6, /* protocol ID */ + 0, /* copy */ + NULL, /* data */ + "IPv6 over IPv6" /* name */ +}; + +/** + * ip6_tunnel_init - register protocol and reserve needed resources + * + * Return: 0 on success + **/ + +int __init ip6_tunnel_init(void) +{ + int i, err; + + ip6ip6_fb_tnl_dev.priv = (void *) &ip6ip6_fb_tnl; + + for (i = 0; i < IP6_TNL_MAXHOOKS; i++) { + INIT_LIST_HEAD(&hooks[i]); + } + if ((err = register_netdev(&ip6ip6_fb_tnl_dev))) + return err; + + inet6_add_protocol(&ip6ip6_protocol); + return 0; +} + +/** + * ip6_tunnel_cleanup - free resources and unregister protocol + **/ + +void ip6_tunnel_cleanup(void) +{ + write_lock_bh(&ip6ip6_kernel_lock); + shutdown = 1; + write_unlock_bh(&ip6ip6_kernel_lock); + flush_scheduled_tasks(); + manage_kernel_tnls(NULL); + inet6_del_protocol(&ip6ip6_protocol); + unregister_netdev(&ip6ip6_fb_tnl_dev); +} + +#ifdef MODULE +module_init(ip6_tunnel_init); +module_exit(ip6_tunnel_cleanup); +#endif + +#if defined(CONFIG_IPV6_MOBILITY_HA_MODULE) || defined(CONFIG_IPV6_MOBILITY_MN_MODULE) +EXPORT_SYMBOL(ip6ip6_tnl_register_hook); +EXPORT_SYMBOL(ip6ip6_tnl_unregister_hook); +#endif +#ifdef CONFIG_IPV6_MOBILITY_HA_MODULE +EXPORT_SYMBOL(ip6ip6_tnl_dec_max_kdev_count); +EXPORT_SYMBOL(ip6ip6_tnl_inc_max_kdev_count); +EXPORT_SYMBOL(ip6ip6_tnl_dec_min_kdev_count); +EXPORT_SYMBOL(ip6ip6_tnl_inc_min_kdev_count); +EXPORT_SYMBOL(ip6ip6_kernel_tnl_add); +EXPORT_SYMBOL(ip6ip6_kernel_tnl_del); +EXPORT_SYMBOL(ip6ip6_tnl_lookup); +#endif +#ifdef CONFIG_IPV6_MOBILITY_MN_MODULE +EXPORT_SYMBOL(ip6ip6_tnl_create); +EXPORT_SYMBOL(ip6ip6_tnl_change); +#endif + diff -uprN linux-2.4.25.old/net/ipv6/mipglue.c linux-2.4.25/net/ipv6/mipglue.c --- linux-2.4.25.old/net/ipv6/mipglue.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.25/net/ipv6/mipglue.c 2004-06-26 11:29:30.000000000 +0100 @@ -0,0 +1,63 @@ +/* + * Glue for Mobility support integration to IPv6 + * + * Authors: + * Antti Tuominen + * + * $Id$ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + */ + +#include + +#include +#include +#include +#include + +extern int ip6_tlvopt_unknown(struct sk_buff *skb, int optoff); + +/* Initialize all zero */ +struct mipv6_callable_functions mipv6_functions = { NULL }; + +/* Sets mipv6_functions struct to zero to invalidate all successive + * calls to mipv6 functions. Used on module unload. */ + +void mipv6_invalidate_calls(void) +{ + memset(&mipv6_functions, 0, sizeof(mipv6_functions)); +} + + +/* Selects correct handler for tlv encoded destination option. Called + * by ip6_parse_tlv. Checks if mipv6 calls are valid before calling. */ + +int mipv6_handle_dstopt(struct sk_buff *skb, int optoff) +{ + int ret; + + switch (skb->nh.raw[optoff]) { + case MIPV6_TLV_HOMEADDR: + ret = MIPV6_CALLFUNC(mipv6_handle_homeaddr, 0)(skb, optoff); + break; + default: + /* Should never happen */ + printk(KERN_ERR __FILE__ ": Invalid destination option code (%d)\n", + skb->nh.raw[optoff]); + ret = 1; + break; + } + + /* If mipv6 handlers are not valid, pass the packet to + * ip6_tlvopt_unknown() for correct handling. */ + if (!ret) + return ip6_tlvopt_unknown(skb, optoff); + + return ret; +} + diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/Config.in linux-2.4.25/net/ipv6/mobile_ip6/Config.in --- linux-2.4.25.old/net/ipv6/mobile_ip6/Config.in 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.25/net/ipv6/mobile_ip6/Config.in 2004-06-26 11:29:30.000000000 +0100 @@ -0,0 +1,12 @@ +# +# Mobile IPv6 Configuration +# +dep_tristate ' IPv6: Mobility Support (Correspondent Node)' CONFIG_IPV6_MOBILITY $CONFIG_IPV6 +if [ "$CONFIG_IPV6_IPV6_TUNNEL" != "n" ]; then + dep_tristate ' MIPv6: Mobile Node Support' CONFIG_IPV6_MOBILITY_MN $CONFIG_IPV6_MOBILITY + + dep_tristate ' MIPv6: Home Agent Support' CONFIG_IPV6_MOBILITY_HA $CONFIG_IPV6_MOBILITY +fi +if [ "$CONFIG_IPV6_MOBILITY" != "n" ]; then + bool ' MIPv6: Debug messages' CONFIG_IPV6_MOBILITY_DEBUG +fi diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/Makefile linux-2.4.25/net/ipv6/mobile_ip6/Makefile --- linux-2.4.25.old/net/ipv6/mobile_ip6/Makefile 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.25/net/ipv6/mobile_ip6/Makefile 2004-06-26 11:29:30.000000000 +0100 @@ -0,0 +1,35 @@ +# +# Makefile for the MIPL Mobile IPv6 for Linux. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# + + +O_TARGET := mip6_base.o + +list-multi := mip6_ha.o mip6_mn.o + +obj-y := hashlist.o bcache.o mobhdr_common.o stats.o exthdrs.o \ + rr_crypto.o hmac.o auth_opt.o mipv6_icmp.o module_cn.o + +obj-m := $(O_TARGET) + +mip6_ha-objs := halist.o mipv6_icmp_ha.o tunnel_ha.o \ + ndisc_ha.o ha.o module_ha.o + +mip6_mn-objs := mipv6_icmp_mn.o ioctl_mn.o tunnel_mn.o \ + mdetect.o bul.o multiaccess_ctl.o mobhdr_mn.o mn.o \ + module_mn.o + +obj-$(CONFIG_IPV6_MOBILITY_HA) += mip6_ha.o +obj-$(CONFIG_IPV6_MOBILITY_MN) += mip6_mn.o + +include $(TOPDIR)/Rules.make + +mip6_ha.o: $(mip6_ha-objs) + $(LD) -r -o $@ $(mip6_ha-objs) + +mip6_mn.o: $(mip6_mn-objs) + $(LD) -r -o $@ $(mip6_mn-objs) diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/README linux-2.4.25/net/ipv6/mobile_ip6/README --- linux-2.4.25.old/net/ipv6/mobile_ip6/README 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.25/net/ipv6/mobile_ip6/README 2004-06-26 11:29:30.000000000 +0100 @@ -0,0 +1,15 @@ +MIPL Mobile IPv6 for Linux + +More information at http://www.mipl.mediapoli.com/. + +To join MIPL Mobile IPv6 for Linux mailing lists go to: + + http://www.mipl.mediapoli.com/cgi-bin/mailman/listinfo + +Or send mail with subject "subscribe" for the general list to: + + mipl-request@list.mipl.mediapoli.com + +or for the developer list to: + + mipl-devel-request@list.mail.mediapoli.com diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/auth_opt.c linux-2.4.25/net/ipv6/mobile_ip6/auth_opt.c --- linux-2.4.25.old/net/ipv6/mobile_ip6/auth_opt.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.25/net/ipv6/mobile_ip6/auth_opt.c 2004-06-26 11:29:30.000000000 +0100 @@ -0,0 +1,121 @@ +/* + * MIPv6 Binding Authentication Data Option functions + * + * Authors: + * Henrik Petander + * + * $Id$ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include + +#include "debug.h" +#include "hmac.h" +#include "mobhdr.h" + +#define DBG_KEY 5 + +int mipv6_auth_build(struct in6_addr *cn_addr, struct in6_addr *coa, + __u8 *mh, __u8 *aud_data, __u8 *k_bu) +{ + /* First look up the peer from sadb based on his address */ + struct ah_processing ahp; + + /* Don't add any other options or this system is screwed */ + + __u8 buf[MAX_HASH_LENGTH]; + + + if (!k_bu) { + DEBUG(DBG_ERROR, "k_bu missing, aborting"); + return -1; + } + DEBUG(DBG_KEY, "Key for building authenticator:"); + debug_print_buffer(DBG_KEY, k_bu, HMAC_SHA1_KEY_SIZE); + + if (ah_hmac_sha1_init(&ahp, k_bu, HMAC_SHA1_KEY_SIZE) < 0) { + DEBUG(DBG_ERROR, "Failed to initialize hmac sha1"); + return -1; + } + + DEBUG(DBG_KEY, "coa: "); + debug_print_buffer(DBG_KEY, coa, 16); + DEBUG(DBG_KEY, "cn_addr: "); + debug_print_buffer(DBG_KEY, cn_addr, 16); + DEBUG(DBG_KEY, "MH contents: "); + debug_print_buffer(DBG_KEY, mh, aud_data - mh); + + /* First the common part */ + ah_hmac_sha1_loop(&ahp, coa, sizeof(struct in6_addr)); + ah_hmac_sha1_loop(&ahp, cn_addr, sizeof(struct in6_addr)); + ah_hmac_sha1_loop(&ahp, mh, aud_data - mh); + ah_hmac_sha1_result(&ahp, buf); + + memcpy(aud_data, buf, MIPV6_RR_MAC_LENGTH); + + return 0; +} + +int mipv6_auth_check(struct in6_addr *cn_addr, struct in6_addr *coa, + __u8 *opt, __u8 optlen, + struct mipv6_mo_bauth_data *aud, __u8 *k_bu) +{ + int ret = -1; + struct ah_processing ahp; + __u8 htarget[MAX_HASH_LENGTH]; + + /* Look up peer by home address */ + if (!k_bu) { + DEBUG(DBG_ERROR, "k_bu missing, aborting"); + return -1; + } + + DEBUG(DBG_KEY, "Key for checking authenticator:"); + debug_print_buffer(DBG_KEY, k_bu, HMAC_SHA1_KEY_SIZE); + + if (!aud || !coa) { + DEBUG(DBG_INFO, "%s is NULL", aud ? "coa" : "aud"); + goto out; + } + + if (aud->length != MIPV6_RR_MAC_LENGTH) { + DEBUG(DBG_ERROR, + ": Incorrect authentication option length %d", aud->length); + goto out; + } + + if (ah_hmac_sha1_init(&ahp, k_bu, HMAC_SHA1_KEY_SIZE) < 0) { + DEBUG(DBG_ERROR, + "internal error in initialization of authentication algorithm"); + goto out; + } + DEBUG(DBG_KEY, "coa: "); + debug_print_buffer(DBG_KEY, coa, 16); + DEBUG(DBG_KEY, "cn_addr: "); + debug_print_buffer(DBG_KEY, cn_addr, 16); + DEBUG(DBG_KEY, "MH contents: "); + debug_print_buffer(DBG_KEY, opt, (u8*) aud->data - opt); + + ah_hmac_sha1_loop(&ahp, coa, sizeof(struct in6_addr)); + ah_hmac_sha1_loop(&ahp, cn_addr, sizeof(struct in6_addr)); + + /* + * Process MH + options till the start of the authenticator in + * Auth. data option + */ + ah_hmac_sha1_loop(&ahp, opt, (u8 *)aud->data - opt); + ah_hmac_sha1_result(&ahp, htarget); + if (memcmp(htarget, aud->data, MIPV6_RR_MAC_LENGTH) == 0) + ret = 0; + + DEBUG(DBG_ERROR, "returning %d", ret); +out: + return ret; +} diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/bcache.c linux-2.4.25/net/ipv6/mobile_ip6/bcache.c --- linux-2.4.25.old/net/ipv6/mobile_ip6/bcache.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.25/net/ipv6/mobile_ip6/bcache.c 2004-06-26 11:29:30.000000000 +0100 @@ -0,0 +1,746 @@ +/* + * Binding Cache + * + * Authors: + * Juha Mynttinen + * + * $Id$ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +/* + * Changes: + * + * Nanno Langstraat : Timer code cleaned up, active socket + * test rewritten + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bcache.h" +#include "hashlist.h" +#include "debug.h" +#include "mobhdr.h" +#include "tunnel.h" +#include "config.h" + +#define TIMERDELAY HZ/10 + +struct mipv6_bcache { + struct hashlist *entries; + __u32 size; + struct timer_list callback_timer; +}; + +struct in6_addr_pair { + struct in6_addr *a1; + struct in6_addr *a2; +}; + +static rwlock_t bcache_lock = RW_LOCK_UNLOCKED; + +static struct mipv6_bcache bcache; + +static int bcache_proc_info(char *buffer, char **start, off_t offset, + int length); + +#define MIPV6_BCACHE_HASHSIZE 32 + +/* Moment of transmission of a BR, in seconds before bcache entry expiry */ +#define BCACHE_BR_SEND_LEAD 3 + +#define MIPV6_MAX_BRR 3 /* Send 3 BRRs before deleting BC entry */ +#define MIPV6_BRR_RATE HZ /* Send BRRs once per second */ + +/* + * Internal functions. + */ + +struct cache_entry_iterator_args { + struct mipv6_bce **entry; +}; + +static int find_first_cache_entry_iterator(void *data, void *args, + unsigned long *lifetime) +{ + struct mipv6_bce *entry = + (struct mipv6_bce *) data; + struct cache_entry_iterator_args *state = + (struct cache_entry_iterator_args *) args; + + ASSERT(entry != NULL); + + if (entry->type == CACHE_ENTRY) { + *(state->entry) = entry; + return ITERATOR_STOP; /* stop iteration */ + } else { + return ITERATOR_CONT; /* continue iteration */ + } +} + + +/* + * Get memory for a new bcache entry. If bcache is full, a cache + * entry may be deleted to get space for a home registration, but not + * vice versa. + */ +static struct mipv6_bce *mipv6_bce_alloc(__u8 type) +{ + struct mipv6_bce *entry; + struct cache_entry_iterator_args args; + + DEBUG_FUNC(); + + entry = (struct mipv6_bce *) + hashlist_alloc(bcache.entries, SLAB_ATOMIC); + + /* Cache replacement policy: always replace the CACHE_ENTRY + closest to expiration. Type HOME_REGISTRATION entry may + never be deleted before expiration. */ + if (entry == NULL) { + /* cache full, try to delete a CACHE_ENTRY */ + args.entry = &entry; + hashlist_iterate(bcache.entries, &args, + find_first_cache_entry_iterator); + if (entry == NULL) + return NULL; + hashlist_delete(bcache.entries, + (struct hashlist_entry *)entry); + entry = (struct mipv6_bce *) + hashlist_alloc(bcache.entries, SLAB_ATOMIC); + } + return entry; +} + +/* + * Frees entry's memory allocated with mipv6_bce_alloc + */ +static void mipv6_bce_free(struct mipv6_bce *entry) +{ + hashlist_free(bcache.entries, (void *) entry); +} + +/* + * Removes all expired entries + */ +static void expire(void) +{ + struct mipv6_bce *entry; + struct br_addrs { + struct in6_addr daddr; + struct in6_addr saddr; + struct br_addrs *next; + }; + struct br_addrs *br_info = NULL; + + DEBUG_FUNC(); + + write_lock(&bcache_lock); + + while ((entry = (struct mipv6_bce *) + hashlist_get_first(bcache.entries)) != NULL) { + struct rt6_info *rt; + if (time_after_eq(jiffies, entry->callback_time)) { + + DEBUG(DBG_INFO, "an entry expired"); + + if (entry->type & HOME_REGISTRATION) { + mip6_fn.proxy_del(&entry->home_addr, entry); + } + hashlist_delete(bcache.entries, (void *)entry); + mipv6_bce_free(entry); + entry = NULL; + } else if (entry->br_callback_time != 0 && + time_after_eq(jiffies, entry->br_callback_time) && + entry->br_count < MIPV6_MAX_BRR && + (rt = rt6_lookup(&entry->home_addr, &entry->our_addr, 0, 0)) != NULL){ + /* Do we have a destination cache entry for the home address */ + if (rt->rt6i_flags & RTF_CACHE) { + struct br_addrs *tmp; + tmp = br_info; + DEBUG(DBG_INFO, + "bcache entry recently used. Sending BR."); + /* queue for sending */ + br_info = kmalloc(sizeof(struct br_addrs), + GFP_ATOMIC); + if (br_info) { + ipv6_addr_copy(&br_info->saddr, + &entry->our_addr); + ipv6_addr_copy(&br_info->daddr, + &entry->home_addr); + br_info->next = tmp; + entry->last_br = jiffies; + entry->br_callback_time = jiffies + MIPV6_BRR_RATE; + entry->br_count++; + } else { + br_info = tmp; + DEBUG(DBG_ERROR, "Out of memory"); + } + + } else + entry->br_callback_time = 0; + dst_release(&rt->u.dst); + } else { + entry->br_callback_time = 0; + break; + } + } + write_unlock(&bcache_lock); + + while (br_info) { + struct br_addrs *tmp = br_info->next; + if (mipv6_send_brr(&br_info->saddr, &br_info->daddr, NULL) < 0) + DEBUG(DBG_WARNING, + "BR send for %x:%x:%x:%x:%x:%x:%x:%x failed", + NIPV6ADDR(&br_info->daddr)); + kfree(br_info); + br_info = tmp; + } +} + +static void set_timer(void) +{ + struct mipv6_bce *entry; + unsigned long callback_time; + + DEBUG_FUNC(); + + entry = (struct mipv6_bce *) + hashlist_get_first(bcache.entries); + if (entry != NULL) { + if (entry->br_callback_time > 0 && + time_after(entry->br_callback_time, jiffies)) + callback_time = entry->br_callback_time; + else if (time_after(entry->callback_time, jiffies)) + callback_time = entry->callback_time; + else { + DEBUG(DBG_WARNING, + "bcache timer attempted to schedule" + " for a historical jiffies count!"); + callback_time = jiffies + TIMERDELAY; + } + + DEBUG(DBG_INFO, "setting timer to now"); + mod_timer(&bcache.callback_timer, callback_time); + } else { + del_timer(&bcache.callback_timer); + DEBUG(DBG_INFO, "BC empty, not setting a new timer"); + } +} + +/* + * The function that is scheduled to do the callback functions. May be + * modified e.g to allow Binding Requests, now only calls expire() and + * schedules a new timer. + */ +static void timer_handler(unsigned long dummy) +{ + expire(); + write_lock(&bcache_lock); + set_timer(); + write_unlock(&bcache_lock); +} + +/* + * Interface functions visible to other modules + */ + +/** + * mipv6_bcache_add - add Binding Cache entry + * @ifindex: interface index + * @our_addr: own address + * @home_addr_org: MN's home address + * @coa: MN's care-of address + * @lifetime: lifetime for this binding + * @prefix: prefix length + * @seq: sequence number + * @flags: flags received in BU + * @type: type of entry + * + * Adds an entry for this @home_addr_org in the Binding Cache. If entry + * already exists, old entry is updated. @type may be %CACHE_ENTRY or + * %HOME_REGISTRATION. + **/ +int mipv6_bcache_add(int ifindex, + struct in6_addr *our_addr, + struct in6_addr *home_addr, + struct in6_addr *coa, + __u32 lifetime, __u16 seq, __u8 flags, __u8 type) +{ + struct mipv6_bce *entry; + int update = 0; + int create_tunnel = 0; + unsigned long now = jiffies; + struct in6_addr_pair hashkey; + int ret = -1; + + DEBUG_FUNC(); + + hashkey.a1 = home_addr; + hashkey.a2 = our_addr; + + write_lock(&bcache_lock); + + if (type == HOME_REGISTRATION && !(mip6node_cnf.capabilities&CAP_HA)) + return 0; + + if (unlikely(bcache.entries == NULL)) { + ret = -ENOMEM; + goto err; + } + + if ((entry = (struct mipv6_bce *) + hashlist_get(bcache.entries, &hashkey)) != NULL) { + /* if an entry for this home_addr exists (with smaller + * seq than the new seq), update it by removing it + * first + */ + if (!MIPV6_SEQ_GT(seq, entry->seq)) { + DEBUG(DBG_INFO, "smaller seq than existing, not updating"); + goto out; + } + DEBUG(DBG_INFO, "updating an existing entry"); + update = 1; + + /* H-flag is already checked in BU handler. */ + /* XXX: Should we care about the other flags?*/ + if (flags != entry->flags) { + DEBUG(DBG_INFO, "entry/BU flag mismatch"); + } + + if (type == HOME_REGISTRATION) { + create_tunnel = (ipv6_addr_cmp(&entry->coa, coa) || + entry->ifindex != ifindex); + } + } else { + /* no entry for this home_addr, try to create a new entry */ + DEBUG(DBG_INFO, "creating a new entry"); + update = 0; + + entry = mipv6_bce_alloc(type); + if (entry == NULL) { + DEBUG(DBG_INFO, "cache full, entry not added"); + goto err; + } + + create_tunnel = (type == HOME_REGISTRATION); + } + + if (create_tunnel) { + if (update) + mip6_fn.proxy_del(&entry->home_addr, entry); + if (mip6_fn.proxy_create(flags, ifindex, coa, our_addr, home_addr) < 0) { + goto err_proxy; + } + } + + ipv6_addr_copy(&(entry->our_addr), our_addr); + ipv6_addr_copy(&(entry->home_addr), home_addr); + ipv6_addr_copy(&(entry->coa), coa); + entry->ifindex = ifindex; + entry->seq = seq; + entry->type = type; + entry->flags = flags; + + entry->last_br = 0; + entry->destunr_count = 0; + entry->callback_time = now + lifetime * HZ; + if (entry->type & HOME_REGISTRATION) + entry->br_callback_time = 0; + else + entry->br_callback_time = now + + (lifetime - BCACHE_BR_SEND_LEAD) * HZ; + + if (update) { + DEBUG(DBG_INFO, "updating entry : %x", entry); + hashlist_reposition(bcache.entries, (void *)entry, + entry->callback_time); + } else { + DEBUG(DBG_INFO, "adding entry: %x", entry); + if ((hashlist_add(bcache.entries, + &hashkey, + entry->callback_time, entry)) < 0) { + + DEBUG(DBG_ERROR, "Hash add failed"); + goto err_hashlist; + } + } + + set_timer(); + +out: + write_unlock(&bcache_lock); + return 0; + +err_hashlist: + if (create_tunnel) { + mip6_fn.proxy_del(home_addr, entry); + } +err_proxy: + if (update) { + hashlist_delete(bcache.entries, (void *)entry); + } + mipv6_bce_free(entry); +err: + write_unlock(&bcache_lock); + return ret; +} + +int mipv6_bcache_icmp_err(struct in6_addr *home_addr, + struct in6_addr *our_addr, + int destunr_count) +{ + struct mipv6_bce *entry; + struct in6_addr_pair hashkey; + + int ret = -ENOENT; + + DEBUG_FUNC(); + + hashkey.a1 = home_addr; + hashkey.a2 = our_addr; + + write_lock(&bcache_lock); + if (unlikely(bcache.entries == NULL)) { + ret = -ENOMEM; + goto err; + } + + if ((entry = (struct mipv6_bce *) + hashlist_get(bcache.entries, &hashkey)) != NULL) { + entry->last_destunr = jiffies; + entry->destunr_count = destunr_count; + ret = 0; + } +err: + write_unlock(&bcache_lock); + return ret; +} + + +/** + * mipv6_bcache_delete - delete Binding Cache entry + * @home_addr: MN's home address + * @our_addr: our address + * @type: type of entry + * + * Deletes an entry associated with @home_addr from Binding Cache. + * Valid values for @type are %CACHE_ENTRY, %HOME_REGISTRATION and + * %ANY_ENTRY. %ANY_ENTRY deletes any type of entry. + **/ +int mipv6_bcache_delete(struct in6_addr *home_addr, + struct in6_addr *our_addr, __u8 type) +{ + struct mipv6_bce *entry; + struct in6_addr_pair hashkey; + int err = 0; + + DEBUG_FUNC(); + + if (home_addr == NULL || our_addr == NULL) { + DEBUG(DBG_INFO, "error in arguments"); + return -EINVAL; + } + + hashkey.a1 = home_addr; + hashkey.a2 = our_addr; + + write_lock(&bcache_lock); + + if (unlikely(bcache.entries == NULL) || + (entry = (struct mipv6_bce *) + hashlist_get(bcache.entries, &hashkey)) == NULL || + !(entry->type & type)) { + DEBUG(DBG_INFO, "No matching entry found"); + err = -ENOENT; + goto out; + } + + hashlist_delete(bcache.entries, (void *) entry); + mipv6_bce_free(entry); + + set_timer(); +out: + write_unlock(&bcache_lock); + return err; +} + +/** + * mipv6_bcache_exists - check if entry exists + * @home_addr: home address to check + * @our_addr: our address + * + * Determines if a binding exists for @home_addr. Returns type of the + * entry or negative if entry does not exist. + **/ +int mipv6_bcache_exists(struct in6_addr *home_addr, + struct in6_addr *our_addr) +{ + struct mipv6_bce *entry; + struct in6_addr_pair hashkey; + int type = -ENOENT; + + DEBUG_FUNC(); + + if (home_addr == NULL || our_addr == NULL) + return -EINVAL; + + hashkey.a1 = home_addr; + hashkey.a2 = our_addr; + + read_lock(&bcache_lock); + if (likely(bcache.entries != NULL) && + (entry = (struct mipv6_bce *) + hashlist_get(bcache.entries, &hashkey)) != NULL) { + type = entry->type; + } + read_unlock(&bcache_lock); + + return type; +} + +/** + * mipv6_bcache_get - get entry from Binding Cache + * @home_addr: home address to search + * @our_addr: our address + * @entry: pointer to buffer + * + * Gets a copy of Binding Cache entry for @home_addr. If entry + * exists entry is copied to @entry and zero is returned. + * Otherwise returns negative. + **/ +int mipv6_bcache_get(struct in6_addr *home_addr, + struct in6_addr *our_addr, + struct mipv6_bce *entry) +{ + struct mipv6_bce *entry2; + struct in6_addr_pair hashkey; + int ret = -ENOENT; + + DEBUG_FUNC(); + + if (home_addr == NULL || our_addr == NULL || entry == NULL) + return -EINVAL; + + hashkey.a1 = home_addr; + hashkey.a2 = our_addr; + + read_lock_bh(&bcache_lock); + + entry2 = (struct mipv6_bce *) + hashlist_get(bcache.entries, &hashkey); + if (entry2 != NULL) { + memcpy(entry, entry2, sizeof(struct mipv6_bce)); + ret = 0; + } + read_unlock_bh(&bcache_lock); + return ret; +} + +int mipv6_bcache_iterate(hashlist_iterator_t func, void *args) +{ + int ret; + + read_lock_bh(&bcache_lock); + ret = hashlist_iterate(bcache.entries, args, func); + read_unlock_bh(&bcache_lock); + + return ret; +} + +/* + * Proc-filesystem functions + */ + +#define BC_INFO_LEN 80 + +struct procinfo_iterator_args { + char *buffer; + int offset; + int length; + int skip; + int len; +}; + +static int procinfo_iterator(void *data, void *args, unsigned long *pref) +{ + struct procinfo_iterator_args *arg = + (struct procinfo_iterator_args *) args; + struct mipv6_bce *entry = + (struct mipv6_bce *) data; + + ASSERT(entry != NULL); + + if (arg->skip < arg->offset / BC_INFO_LEN) { + arg->skip++; + return ITERATOR_CONT; + } + + if (arg->len >= arg->length) + return ITERATOR_CONT; + + /* HoA CoA CallbackInSecs Type */ + arg->len += sprintf(arg->buffer + arg->len, + "%08x%08x%08x%08x %08x%08x%08x%08x %010lu %02d\n", + ntohl(entry->home_addr.s6_addr32[0]), + ntohl(entry->home_addr.s6_addr32[1]), + ntohl(entry->home_addr.s6_addr32[2]), + ntohl(entry->home_addr.s6_addr32[3]), + ntohl(entry->coa.s6_addr32[0]), + ntohl(entry->coa.s6_addr32[1]), + ntohl(entry->coa.s6_addr32[2]), + ntohl(entry->coa.s6_addr32[3]), + ((entry->callback_time) - jiffies) / HZ, + (int) entry->type); + + return ITERATOR_CONT; +} + + /* + * Callback function for proc filesystem. + */ +static int bcache_proc_info(char *buffer, char **start, off_t offset, + int length) +{ + struct procinfo_iterator_args args; + + DEBUG_FUNC(); + + args.buffer = buffer; + args.offset = offset; + args.length = length; + args.skip = 0; + args.len = 0; + + read_lock_bh(&bcache_lock); + hashlist_iterate(bcache.entries, &args, procinfo_iterator); + read_unlock_bh(&bcache_lock); + + *start = buffer; + if (offset) + *start += offset % BC_INFO_LEN; + + args.len -= offset % BC_INFO_LEN; + + if (args.len > length) + args.len = length; + if (args.len < 0) + args.len = 0; + + return args.len; +} + +static int bcache_compare(void *data, void *hashkey) +{ + struct in6_addr_pair *p = (struct in6_addr_pair *) hashkey; + struct mipv6_bce *e = (struct mipv6_bce *) data; + + if (ipv6_addr_cmp(&e->home_addr, p->a1) == 0 + && ipv6_addr_cmp(&e->our_addr, p->a2) == 0) + return 0; + else + return -1; +} + +static __u32 bcache_hash(void *hashkey) +{ + struct in6_addr_pair *p = (struct in6_addr_pair *) hashkey; + + return p->a1->s6_addr32[0] ^ p->a1->s6_addr32[1] ^ + p->a2->s6_addr32[2] ^ p->a2->s6_addr32[3]; +} + +/* + * Initialization and shutdown functions + */ + +int __init mipv6_bcache_init(__u32 size) +{ + if (size < 1) { + DEBUG(DBG_ERROR, "Binding cache size must be at least 1"); + return -EINVAL; + } + bcache.entries = hashlist_create(MIPV6_BCACHE_HASHSIZE, size, + sizeof(struct mipv6_bce), + "mip6_bcache", NULL, NULL, + bcache_compare, bcache_hash); + + if (bcache.entries == NULL) { + DEBUG(DBG_ERROR, "Failed to initialize hashlist"); + return -ENOMEM; + } + + init_timer(&bcache.callback_timer); + bcache.callback_timer.data = 0; + bcache.callback_timer.function = timer_handler; + bcache.size = size; + + proc_net_create("mip6_bcache", 0, bcache_proc_info); + + DEBUG(DBG_INFO, "Binding cache initialized"); + return 0; +} + +static int +bce_cleanup_iterator(void *rawentry, void *args, unsigned long *sortkey) +{ + int type = (int) args; + struct mipv6_bce *entry = (struct mipv6_bce *) rawentry; + if (entry->type == type) { + if (entry->type & HOME_REGISTRATION) { + if (unlikely(mip6_fn.proxy_del == NULL)) + DEBUG(DBG_ERROR, "proxy_del unitialized"); + else + mip6_fn.proxy_del(&entry->home_addr, entry); + } + return ITERATOR_DELETE_ENTRY; + } + return ITERATOR_CONT; + +} + +void mipv6_bcache_cleanup(int type) +{ + write_lock_bh(&bcache_lock); + hashlist_iterate(bcache.entries,(void *) type, bce_cleanup_iterator); + write_unlock_bh(&bcache_lock); +} + +int __exit mipv6_bcache_exit(void) +{ + struct hashlist *entries; + + DEBUG_FUNC(); + + proc_net_remove("mip6_bcache"); + + write_lock_bh(&bcache_lock); + DEBUG(DBG_INFO, "Stopping the bcache timer"); + del_timer(&bcache.callback_timer); + hashlist_iterate(bcache.entries,(void *)CACHE_ENTRY, + bce_cleanup_iterator); + + entries = bcache.entries; + bcache.entries = NULL; + write_unlock_bh(&bcache_lock); + + hashlist_destroy(entries); + return 0; +} diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/bcache.h linux-2.4.25/net/ipv6/mobile_ip6/bcache.h --- linux-2.4.25.old/net/ipv6/mobile_ip6/bcache.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.25/net/ipv6/mobile_ip6/bcache.h 2004-06-26 11:29:30.000000000 +0100 @@ -0,0 +1,72 @@ +/* + * MIPL Mobile IPv6 Binding Cache header file + * + * $Id$ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _BCACHE_H +#define _BCACHE_H + +#include +#include +#include "hashlist.h" + +#define CACHE_ENTRY 1 /* this and HOME_REGISTRATION are the entry types */ +#define HOME_REGISTRATION 2 +#define ANY_ENTRY 3 + +#define MIPV6_MAX_DESTUNREACH 5 /* Delete CN BCEs after 5 destination unreachables */ +#define MIPV6_DEST_UNR_IVAL 10 /* What is the max interval of destination + unreacahable error messages for them to be persistent*/ + +struct mipv6_bce { + struct hashlist_entry e; + int ifindex; /* Interface identifier */ + struct in6_addr our_addr; /* our address (as seen by the MN) */ + struct in6_addr home_addr; /* MN home address */ + struct in6_addr coa; /* MN care-of address */ + unsigned long callback_time; /* time of expiration (in jiffies) */ + unsigned long br_callback_time; /* time for sending a BR (in jiffies) */ + int (*callback_function)(struct mipv6_bce *entry); + __u8 type; /* home registration */ + __u8 router; /* mn is router */ + __u8 flags; /* flags received in BU */ + __u16 seq; /* sequence number */ + unsigned long last_br; /* time when last BR sent */ + unsigned long last_destunr; /* time when last ICMP destination unreachable received */ + int br_count; /* How many BRRs have sent */ + int destunr_count; /* Number of destination unreachables received */ +}; + +int mipv6_bcache_add(int ifindex, struct in6_addr *our_addr, + struct in6_addr *home_addr, struct in6_addr *coa, + __u32 lifetime, __u16 seq, __u8 flags, __u8 type); + +int mipv6_bcache_icmp_err(struct in6_addr *home_addr, + struct in6_addr *our_addr, + int destunr_count); + +int mipv6_bcache_delete(struct in6_addr *home_addr, struct in6_addr *our_addr, + __u8 type); + +int mipv6_bcache_exists(struct in6_addr *home_addr, + struct in6_addr *our_addr); + +int mipv6_bcache_get(struct in6_addr *home_addr, + struct in6_addr *our_addr, + struct mipv6_bce *entry); + +int mipv6_bcache_iterate(int (*func)(void *, void *, unsigned long *), void *args); + +void mipv6_bcache_cleanup(int type); + +int mipv6_bcache_init(__u32 size); + +int mipv6_bcache_exit(void); + +#endif /* _BCACHE_H */ diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/bul.c linux-2.4.25/net/ipv6/mobile_ip6/bul.c --- linux-2.4.25.old/net/ipv6/mobile_ip6/bul.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.25/net/ipv6/mobile_ip6/bul.c 2004-06-26 11:29:30.000000000 +0100 @@ -0,0 +1,634 @@ +/* + * Binding update list + * + * Authors: + * Juha Mynttinen + * + * $Id$ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +/* + * Changes: + * + * Nanno Langstraat : Timer code cleaned up + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bul.h" +#include "debug.h" +#include "hashlist.h" +#include "tunnel_mn.h" +#include "mobhdr.h" + +#define MIPV6_BUL_HASHSIZE 32 + +rwlock_t bul_lock = RW_LOCK_UNLOCKED; + +struct mipv6_bul { + struct hashlist *entries; + struct timer_list callback_timer; +}; + +static struct mipv6_bul bul; + +struct in6_addr_pair { + struct in6_addr *a1; + struct in6_addr *a2; +}; + +/********************************************************************** + * + * Private functions + * + **********************************************************************/ + +static int bul_compare(void *data, void *hashkey) +{ + struct in6_addr_pair *p = (struct in6_addr_pair *)hashkey; + struct mipv6_bul_entry *e = (struct mipv6_bul_entry *)data; + + if (ipv6_addr_cmp(&e->cn_addr, p->a1) == 0 + && ipv6_addr_cmp(&e->home_addr, p->a2) == 0) + return 0; + else + return -1; +} + +struct test_keys { + struct in6_addr *addr; + u8 *cookie; +}; + +static int bul_compare_cookie(void *data, void *keys) +{ + struct test_keys *p = (struct test_keys *)keys; + struct mipv6_bul_entry *e = (struct mipv6_bul_entry *)data; + + if (ipv6_addr_cmp(&e->cn_addr, p->addr) == 0 && e->rr + && memcmp(&e->rr->cot_cookie, p->cookie, 8) == 0) + return 0; + else + return -1; +} + +static u32 bul_hash(void *hashkey) +{ + struct in6_addr_pair *p = (struct in6_addr_pair *)hashkey; + + return p->a1->s6_addr32[0] ^ + p->a1->s6_addr32[1] ^ + p->a1->s6_addr32[2] ^ + p->a1->s6_addr32[3]; +} + +static int bul_proc_info(char *buffer, char **start, off_t offset, + int length); + +static struct mipv6_bul_entry *mipv6_bul_get_entry(void) +{ + DEBUG_FUNC(); + return ((struct mipv6_bul_entry *) + hashlist_alloc(bul.entries, SLAB_ATOMIC)); +} + +static void mipv6_bul_entry_free(struct mipv6_bul_entry *entry) +{ + DEBUG_FUNC(); + + if (entry->rr) { + if (entry->rr->kbu) + kfree(entry->rr->kbu); + kfree(entry->rr); + } + if (entry->ops) + kfree(entry->ops); + hashlist_free(bul.entries, (void *)entry); +} + +static __inline__ int del_bul_entry_tnl(struct mipv6_bul_entry *entry) +{ + if (entry->flags & MIPV6_BU_F_HOME) { + return mipv6_mv_tnl_to_ha(&entry->cn_addr, + &entry->coa, + &entry->home_addr); + } + return 0; +} + +static void timer_update(void) +{ + struct mipv6_bul_entry *entry; + + DEBUG_FUNC(); + + entry = hashlist_get_first(bul.entries); + + while (entry && time_after_eq(jiffies, entry->callback_time)) { + if (time_after_eq(jiffies, entry->expire) || + entry->callback(entry) != 0) { + /* + * Either the entry has expired, or the callback + * indicated that it should be deleted. + */ + hashlist_delete(bul.entries, (void *)entry); + + del_bul_entry_tnl(entry); + mipv6_bul_entry_free(entry); + DEBUG(DBG_INFO, "Entry deleted (was expired) from " + "binding update list"); + } else { + /* move entry to its right place in the hashlist */ + DEBUG(DBG_INFO, "Rescheduling"); + hashlist_reposition(bul.entries, (void *)entry, + entry->callback_time); + } + entry = (struct mipv6_bul_entry *) + hashlist_get_first(bul.entries); + } + + if (entry == NULL) { + DEBUG(DBG_INFO, "bul empty, not setting a new timer"); + del_timer(&bul.callback_timer); + } else { + mod_timer(&bul.callback_timer, entry->callback_time); + } +} + +static void timer_handler(unsigned long dummy) +{ + DEBUG_FUNC(); + + write_lock(&bul_lock); + timer_update(); + write_unlock(&bul_lock); +} + +/********************************************************************** + * + * Public interface functions + * + **********************************************************************/ + +/** + * mipv6_bul_iterate - apply interator function to all entries + * @func: function to apply + * @args: extra arguments for iterator + * + * Applies @func for each entry in Binding Update List. Extra + * arguments given in @args are also passed to the iterator function. + * Caller must hold @bul_lock. + **/ +int mipv6_bul_iterate(hashlist_iterator_t func, void *args) +{ + DEBUG_FUNC(); + + return hashlist_iterate(bul.entries, args, func); +} + +/** + * mipv6_bul_exists - check if Binding Update List entry exists + * @cn: address to check + * + * Checks if Binding Update List has an entry for @cn. Returns true + * if entry exists, false otherwise. Caller may not hold @bul_lock. + **/ +int mipv6_bul_exists(struct in6_addr *cn, struct in6_addr *haddr) +{ + int exists; + struct in6_addr_pair hashkey; + + DEBUG_FUNC(); + + hashkey.a1 = cn; + hashkey.a2 = haddr; + + read_lock_bh(&bul_lock); + + if (unlikely(bul.entries == NULL)) + exists = 0; + else + exists = (hashlist_get(bul.entries, &hashkey) != NULL); + + read_unlock_bh(&bul_lock); + return exists; +} + +/** + * mipv6_bul_get - get Binding Update List entry + * @cn_addr: CN address to search + * @home_addr: home address to search + * + * Returns Binding Update List entry for @cn_addr if it exists. + * Otherwise returns %NULL. Caller must hold @bul_lock. + **/ +struct mipv6_bul_entry *mipv6_bul_get(struct in6_addr *cn_addr, + struct in6_addr *home_addr) +{ + struct mipv6_bul_entry *entry; + struct in6_addr_pair hashkey; + + DEBUG_FUNC(); + + if (unlikely(bul.entries == NULL)) { + return NULL; + } + hashkey.a1 = cn_addr; + hashkey.a2 = home_addr; + + entry = (struct mipv6_bul_entry *) + hashlist_get(bul.entries, &hashkey); + + return entry; +} + +struct mipv6_bul_entry *mipv6_bul_get_by_ccookie( + struct in6_addr *cn_addr, u8 *cookie) +{ + struct test_keys key; + + DEBUG_FUNC(); + + if (unlikely(bul.entries == NULL)) + return NULL; + key.addr = cn_addr; + key.cookie = cookie; + + return (struct mipv6_bul_entry *) + hashlist_get_ex(bul.entries, &key, + bul_compare_cookie); +} + +/** + * mipv6_bul_reschedule - reschedule Binding Update List entry + * @entry: entry to reschedule + * + * Reschedules a Binding Update List entry. Must be called after + * modifying entry lifetime. Caller must hold @bul_lock (write). + **/ +void mipv6_bul_reschedule(struct mipv6_bul_entry *entry) +{ + DEBUG_FUNC(); + + hashlist_reposition(bul.entries, + (void *)entry, + entry->callback_time); + timer_update(); +} + +/** + * mipv6_bul_add - add binding update to Binding Update List + * @cn_addr: IPv6 address where BU was sent + * @home_addr: Home address for this binding + * @coa: Care-of address for this binding + * @lifetime: expiration time of the binding in seconds + * @seq: sequence number of the BU + * @flags: %MIPV6_BU_F_* flags + * @callback: callback function called on expiration + * @callback_time: expiration time for callback + * @state: binding send state + * @delay: retransmission delay + * @maxdelay: retransmission maximum delay + * @ops: Mobility header options for BU + * @rr: Return routability information + * + * Adds a binding update sent to @cn_addr for @home_addr to the + * Binding Update List. If entry already exists, it is updated. + * Entry is set to expire in @lifetime seconds. Entry has a callback + * function @callback that is called at @callback_time. Entry @state + * controls resending of this binding update and it can be set to + * %ACK_OK, %RESEND_EXP or %ACK_ERROR. Returns a pointer to the newly + * created or updated entry. Caller must hold @bul_lock (write). + **/ +struct mipv6_bul_entry *mipv6_bul_add( + struct in6_addr *cn_addr, struct in6_addr *home_addr, + struct in6_addr *coa, + __u32 lifetime, __u16 seq, __u8 flags, + int (*callback)(struct mipv6_bul_entry *entry), + __u32 callback_time, + __u8 state, __u32 delay, __u32 maxdelay, + struct mipv6_mh_opt *ops, + struct mipv6_rr_info *rr) +{ + struct mipv6_bul_entry *entry; + int update = 0; + struct in6_addr_pair hashkey; + + DEBUG_FUNC(); + + if (unlikely(bul.entries == NULL)) + return NULL; + + if (cn_addr == NULL || home_addr == NULL || coa == NULL || + lifetime < 0 || callback == NULL || callback_time < 0 || + (state != ACK_OK && state != RESEND_EXP && state != ACK_ERROR) || + delay < 0 || maxdelay < 0) { + DEBUG(DBG_ERROR, "invalid arguments"); + return NULL; + } + DEBUG(DBG_INFO, "cn_addr: %x:%x:%x:%x:%x:%x:%x:%x, " + "home_addr: %x:%x:%x:%x:%x:%x:%x:%x" + "coaddr: %x:%x:%x:%x:%x:%x:%x:%x", NIPV6ADDR(cn_addr), + NIPV6ADDR(home_addr), NIPV6ADDR(coa)); + hashkey.a1 = cn_addr; + hashkey.a2 = home_addr; + + /* + * decide whether to add a new entry or update existing, also + * check if there's room for a new entry when adding a new + * entry (latter is handled by mipv6_bul_get_entry() + */ + if ((entry = (struct mipv6_bul_entry *) + hashlist_get(bul.entries, &hashkey)) != NULL) { + /* if an entry for this cn_addr exists (with smaller + * seq than the new entry's seq), update it */ + + if (MIPV6_SEQ_GT(seq, entry->seq)) { + DEBUG(DBG_INFO, "updating an existing entry"); + update = 1; + } else { + DEBUG(DBG_INFO, "smaller seq than existing, not updating"); + return NULL; + } + } else { + entry = mipv6_bul_get_entry(); + if (entry == NULL) { + DEBUG(DBG_WARNING, "binding update list full, can't add!!!"); + return NULL; + } + memset(entry, 0, sizeof(*entry)); + /* First BU send happens here, save count in the entry */ + entry->consecutive_sends = 1; + } + + if (!update) { + ipv6_addr_copy(&(entry->cn_addr), cn_addr); + ipv6_addr_copy(&(entry->home_addr), home_addr); + entry->ops = ops; + } + /* Add Return Routability info to bul entry */ + if (rr) { + if(entry->rr) + kfree(entry->rr); + entry->rr = rr; + } + + ipv6_addr_copy(&(entry->coa), coa); + entry->lifetime = lifetime; + if (lifetime) + entry->expire = jiffies + lifetime * HZ; + else if (flags & MIPV6_BU_F_ACK) + entry->expire = jiffies + HOME_RESEND_EXPIRE * HZ; + entry->seq = seq; + entry->flags = flags; + entry->lastsend = jiffies; /* current time = last use of the entry */ + entry->state = state; + entry->delay = delay; + entry->maxdelay = maxdelay; + entry->callback_time = jiffies + callback_time * HZ; + entry->callback = callback; + + if (flags & MIPV6_BU_F_HOME && + mipv6_mv_tnl_to_ha(cn_addr, coa, home_addr)) { + DEBUG(DBG_ERROR, "reconfiguration of the tunnel failed"); + } + if (update) { + DEBUG(DBG_INFO, "updating entry: %x", entry); + hashlist_reposition(bul.entries, (void *)entry, + entry->callback_time); + } else { + DEBUG(DBG_INFO, "adding entry: %x", entry); + + hashkey.a1 = &entry->cn_addr; + hashkey.a2 = &entry->home_addr; + + if ((hashlist_add(bul.entries, &hashkey, + entry->callback_time, + entry)) < 0) { + DEBUG(DBG_ERROR, "Hash add failed"); + mipv6_bul_entry_free(entry); + return NULL; + } + } + timer_update(); + + return entry; +} + +/** + * mipv6_bul_delete - delete Binding Update List entry + * @cn_addr: address for entry to delete + * + * Deletes the entry for @cn_addr from the Binding Update List. + * Returns zero if entry was deleted succesfully, otherwise returns + * negative. Caller may not hold @bul_lock. + **/ +int mipv6_bul_delete(struct in6_addr *cn_addr, struct in6_addr *home_addr) +{ + struct mipv6_bul_entry *entry; + struct in6_addr_pair hashkey; + + DEBUG_FUNC(); + + hashkey.a1 = cn_addr; + hashkey.a2 = home_addr; + + write_lock(&bul_lock); + + if (unlikely(bul.entries == NULL) || + (entry = (struct mipv6_bul_entry *) + hashlist_get(bul.entries, &hashkey)) == NULL) { + write_unlock(&bul_lock); + DEBUG(DBG_INFO, "No such entry"); + return -ENOENT; + } + + hashlist_delete(bul.entries, (void *)entry); + + del_bul_entry_tnl(entry); + + mipv6_bul_entry_free(entry); + timer_update(); + write_unlock(&bul_lock); + + DEBUG(DBG_INFO, "Binding update list entry deleted"); + + return 0; +} + +/********************************************************************** + * + * Proc interface functions + * + **********************************************************************/ + +#define BUL_INFO_LEN 152 + +struct procinfo_iterator_args { + char *buffer; + int offset; + int length; + int skip; + int len; +}; + +static int procinfo_iterator(void *data, void *args, + unsigned long *sortkey) +{ + struct procinfo_iterator_args *arg = + (struct procinfo_iterator_args *)args; + struct mipv6_bul_entry *entry = + (struct mipv6_bul_entry *)data; + unsigned long callback_seconds; + + DEBUG_FUNC(); + + if (entry == NULL) return ITERATOR_ERR; + + if (time_after(jiffies, entry->callback_time)) + callback_seconds = 0; + else + callback_seconds = (entry->callback_time - jiffies) / HZ; + + if (arg->skip < arg->offset / BUL_INFO_LEN) { + arg->skip++; + return ITERATOR_CONT; + } + + if (arg->len >= arg->length) + return ITERATOR_CONT; + + /* CN HoA CoA ExpInSecs SeqNum State Delay MaxDelay CallbackInSecs */ + arg->len += sprintf(arg->buffer + arg->len, + "%08x%08x%08x%08x %08x%08x%08x%08x %08x%08x%08x%08x\n" + "%010lu %05d %02d %010d %010d %010lu\n", + ntohl(entry->cn_addr.s6_addr32[0]), + ntohl(entry->cn_addr.s6_addr32[1]), + ntohl(entry->cn_addr.s6_addr32[2]), + ntohl(entry->cn_addr.s6_addr32[3]), + ntohl(entry->home_addr.s6_addr32[0]), + ntohl(entry->home_addr.s6_addr32[1]), + ntohl(entry->home_addr.s6_addr32[2]), + ntohl(entry->home_addr.s6_addr32[3]), + ntohl(entry->coa.s6_addr32[0]), + ntohl(entry->coa.s6_addr32[1]), + ntohl(entry->coa.s6_addr32[2]), + ntohl(entry->coa.s6_addr32[3]), + (entry->expire - jiffies) / HZ, + entry->seq, entry->state, entry->delay, + entry->maxdelay, callback_seconds); + + return ITERATOR_CONT; +} + + +/* + * Callback function for proc filesystem. + */ +static int bul_proc_info(char *buffer, char **start, off_t offset, + int length) +{ + struct procinfo_iterator_args args; + + DEBUG_FUNC(); + + args.buffer = buffer; + args.offset = offset; + args.length = length; + args.skip = 0; + args.len = 0; + + read_lock_bh(&bul_lock); + hashlist_iterate(bul.entries, &args, procinfo_iterator); + read_unlock_bh(&bul_lock); + + *start = buffer; + if (offset) + *start += offset % BUL_INFO_LEN; + + args.len -= offset % BUL_INFO_LEN; + + if (args.len > length) + args.len = length; + if (args.len < 0) + args.len = 0; + + return args.len; +} + +/********************************************************************** + * + * Code module init/fini functions + * + **********************************************************************/ + +int __init mipv6_bul_init(__u32 size) +{ + DEBUG_FUNC(); + + if (size < 1) { + DEBUG(DBG_CRITICAL, + "Binding update list size must be at least 1"); + return -EINVAL; + } + bul.entries = hashlist_create(MIPV6_BUL_HASHSIZE, size, + sizeof(struct mipv6_bul_entry), + "mip6_bul", NULL, NULL, + bul_compare, bul_hash); + + if (bul.entries == NULL) { + DEBUG(DBG_CRITICAL, "Couldn't allocate memory for " + "hashlist when creating a binding update list"); + return -ENOMEM; + } + init_timer(&bul.callback_timer); + bul.callback_timer.data = 0; + bul.callback_timer.function = timer_handler; + proc_net_create("mip6_bul", 0, bul_proc_info); + DEBUG(DBG_INFO, "Binding update list initialized"); + return 0; +} + +void __exit mipv6_bul_exit() +{ + struct mipv6_bul_entry *entry; + struct hashlist *entries; + + DEBUG_FUNC(); + + proc_net_remove("mip6_bul"); + + write_lock_bh(&bul_lock); + + DEBUG(DBG_INFO, "Stopping the bul timer"); + del_timer(&bul.callback_timer); + + while ((entry = (struct mipv6_bul_entry *) + hashlist_get_first(bul.entries)) != NULL) { + hashlist_delete(bul.entries, (void *)entry); + + del_bul_entry_tnl(entry); + + mipv6_bul_entry_free(entry); + } + entries = bul.entries; + bul.entries = NULL; + write_unlock_bh(&bul_lock); + + hashlist_destroy(entries); + + DEBUG(DBG_INFO, "binding update list destroyed"); +} diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/bul.h linux-2.4.25/net/ipv6/mobile_ip6/bul.h --- linux-2.4.25.old/net/ipv6/mobile_ip6/bul.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.25/net/ipv6/mobile_ip6/bul.h 2004-06-26 11:29:30.000000000 +0100 @@ -0,0 +1,91 @@ +/* + * MIPL Mobile IPv6 Binding Update List header file + * + * $Id$ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _BUL_H +#define _BUL_H + +#include "hashlist.h" + +#define ACK_OK 0x01 +#define RESEND_EXP 0x02 +#define ACK_ERROR 0x04 + +#define HOME_RESEND_EXPIRE 3600 +#define MIPV6_COOKIE_LEN 8 +struct mipv6_rr_info { + /* RR information */ + u16 rr_state; /* State of the RR */ + u16 rr_flags; /* Flags for the RR */ + u8 hot_cookie[MIPV6_COOKIE_LEN]; /* HoT Cookie */ + u8 cot_cookie[MIPV6_COOKIE_LEN]; /* CoT Cookie */ + u8 home_cookie[MIPV6_COOKIE_LEN]; /* Home Cookie */ + u8 careof_cookie[MIPV6_COOKIE_LEN]; /* Careof Cookie */ + u32 lastsend_hoti; /* When HoTI was last sent (jiffies) */ + u32 lastsend_coti; /* When CoTI was last sent (jiffies) */ + u32 home_time; /* when Care-of cookie was received */ + u32 careof_time; /* when Home cookie was received */ + int home_nonce_index; /* Home cookie nonce index */ + int careof_nonce_index; /* Care-of cookie nonce index */ + u8 *kbu; /* Binding authentication key */ +}; +struct mipv6_bul_entry { + struct hashlist_entry e; + struct in6_addr cn_addr; /* CN to which BU was sent */ + struct in6_addr home_addr; /* home address of this binding */ + struct in6_addr coa; /* care-of address of the sent BU */ + + unsigned long expire; /* entry's expiration time (jiffies) */ + __u32 lifetime; /* lifetime sent in this BU */ + __u32 lastsend; /* last time when BU sent (jiffies) */ + __u32 consecutive_sends; /* Number of consecutive BU's sent */ + __u16 seq; /* sequence number of the latest BU */ + __u8 flags; /* BU send flags */ + __u8 state; /* resend state */ + __u32 initdelay; /* initial ack wait */ + __u32 delay; /* current ack wait */ + __u32 maxdelay; /* maximum ack wait */ + + struct mipv6_rr_info *rr; + struct mipv6_mh_opt *ops; /* saved option values */ + + unsigned long callback_time; + int (*callback)(struct mipv6_bul_entry *entry); +}; + +extern rwlock_t bul_lock; + +int mipv6_bul_init(__u32 size); + +void mipv6_bul_exit(void); + +struct mipv6_bul_entry *mipv6_bul_add( + struct in6_addr *cn_addr, struct in6_addr *home_addr, + struct in6_addr *coa, __u32 lifetime, __u16 seq, __u8 flags, + int (*callback)(struct mipv6_bul_entry *entry), __u32 callback_time, + __u8 state, __u32 delay, __u32 maxdelay, struct mipv6_mh_opt *ops, + struct mipv6_rr_info *rr); + +int mipv6_bul_delete(struct in6_addr *cn_addr, struct in6_addr *home_addr); + +int mipv6_bul_exists(struct in6_addr *cnaddr, struct in6_addr *home_addr); + +struct mipv6_bul_entry *mipv6_bul_get(struct in6_addr *cnaddr, + struct in6_addr *home_addr); +struct mipv6_bul_entry *mipv6_bul_get_by_ccookie(struct in6_addr *cn_addr, + u8 *cookie); + +int bul_entry_expired(struct mipv6_bul_entry *bulentry); + +void mipv6_bul_reschedule(struct mipv6_bul_entry *entry); + +int mipv6_bul_iterate(int (*func)(void *, void *, unsigned long *), void *args); + +#endif /* BUL_H */ diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/config.h linux-2.4.25/net/ipv6/mobile_ip6/config.h --- linux-2.4.25.old/net/ipv6/mobile_ip6/config.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.25/net/ipv6/mobile_ip6/config.h 2004-06-26 11:29:30.000000000 +0100 @@ -0,0 +1,72 @@ +/* + * Configuration parameters + * + * $Id$ + */ + +#define MIPV6VERSION "D24" +#define MIPLVERSION "v1.0" + +#define CAP_CN 0x01 +#define CAP_HA 0x02 +#define CAP_MN 0x04 + +struct mip6_conf { + int capabilities; + int debug_level; + int accept_ret_rout; + int max_rtr_reachable_time; + int eager_cell_switching; + int max_num_tunnels; + int min_num_tunnels; + int binding_refresh_advice; + int bu_lladdr; + int bu_keymgm; + int bu_cn_ack; +}; + +extern struct mip6_conf mip6node_cnf; + +struct mipv6_bce; + +struct mip6_func { + void (*bce_home_add) (int ifindex, struct in6_addr *daddr, + struct in6_addr *haddr, struct in6_addr *coa, + struct in6_addr *rep_coa, __u32 lifetime, + __u16 sequence, __u8 flags, __u8 *k_bu); + void (*bce_cache_add) (int ifindex, struct in6_addr *daddr, + struct in6_addr *haddr, struct in6_addr *coa, + struct in6_addr *rep_coa, __u32 lifetime, + __u16 sequence, __u8 flags, __u8 *k_bu); + void (*bce_home_del) (struct in6_addr *daddr, struct in6_addr *haddr, + struct in6_addr *coa, struct in6_addr *rep_coa, + __u16 sequence, __u8 flags, + __u8 *k_bu); + void (*bce_cache_del) (struct in6_addr *daddr, struct in6_addr *haddr, + struct in6_addr *coa, struct in6_addr *rep_coa, + __u16 sequence, __u8 flags, + __u8 *k_bu); + + int (*bce_tnl_rt_add) (struct in6_addr *coa, + struct in6_addr *ha_addr, + struct in6_addr *home_addr); + + void (*bce_tnl_rt_del) (struct in6_addr *coa, + struct in6_addr *ha_addr, + struct in6_addr *home_addr); + + void (*proxy_del) (struct in6_addr *home_addr, struct mipv6_bce *entry); + int (*proxy_create) (int flags, int ifindex, struct in6_addr *coa, + struct in6_addr *our_addr, struct in6_addr *home_addr); + + int (*icmpv6_dhaad_rep_rcv) (struct sk_buff *skb); + int (*icmpv6_dhaad_req_rcv) (struct sk_buff *skb); + int (*icmpv6_pfxadv_rcv) (struct sk_buff *skb); + int (*icmpv6_pfxsol_rcv) (struct sk_buff *skb); + int (*icmpv6_paramprob_rcv) (struct sk_buff *skb); + + int (*mn_use_hao) (struct in6_addr *daddr, struct in6_addr *saddr); + void (*mn_check_tunneled_packet) (struct sk_buff *skb); +}; + +extern struct mip6_func mip6_fn; diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/debug.h linux-2.4.25/net/ipv6/mobile_ip6/debug.h --- linux-2.4.25.old/net/ipv6/mobile_ip6/debug.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.25/net/ipv6/mobile_ip6/debug.h 2004-06-26 11:29:30.000000000 +0100 @@ -0,0 +1,112 @@ +/* + * MIPL Mobile IPv6 Debugging macros and functions + * + * $Id$ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _DEBUG_H +#define _DEBUG_H + +#include + +/* priorities for different debug conditions */ + +#define DBG_CRITICAL 0 /* unrecoverable error */ +#define DBG_ERROR 1 /* error (recoverable) */ +#define DBG_WARNING 2 /* unusual situation but not a real error */ +#define DBG_INFO 3 /* generally useful information */ +#define DBG_EXTRA 4 /* extra information */ +#define DBG_FUNC_ENTRY 6 /* use to indicate function entry and exit */ +#define DBG_DATADUMP 7 /* packet dumps, etc. lots of flood */ + +/** + * NIPV6ADDR - macro for IPv6 addresses + * @addr: Network byte order IPv6 address + * + * Macro for printing IPv6 addresses. Used in conjunction with + * printk() or derivatives (such as DEBUG macro). + **/ +#define NIPV6ADDR(addr) \ + ntohs(((u16 *)addr)[0]), \ + ntohs(((u16 *)addr)[1]), \ + ntohs(((u16 *)addr)[2]), \ + ntohs(((u16 *)addr)[3]), \ + ntohs(((u16 *)addr)[4]), \ + ntohs(((u16 *)addr)[5]), \ + ntohs(((u16 *)addr)[6]), \ + ntohs(((u16 *)addr)[7]) + +#ifdef CONFIG_IPV6_MOBILITY_DEBUG +extern int mipv6_debug; + +/** + * debug_print - print debug message + * @debug_level: message priority + * @fname: calling function's name + * @fmt: printf-style formatting string + * + * Prints a debug message to system log if @debug_level is less or + * equal to @mipv6_debug. Should always be called using DEBUG() + * macro, not directly. + **/ +static void debug_print(int debug_level, const char *fname, const char* fmt, ...) +{ + char s[1024]; + va_list args; + + if (mipv6_debug < debug_level) + return; + + va_start(args, fmt); + vsprintf(s, fmt, args); + printk("mip6[%s]: %s\n", fname, s); + va_end(args); +} + +/** + * debug_print_buffer - print arbitrary buffer to system log + * @debug_level: message priority + * @data: pointer to buffer + * @len: number of bytes to print + * + * Prints @len bytes from buffer @data to system log. @debug_level + * tells on which debug level message gets printed. For + * debug_print_buffer() priority %DBG_DATADUMP should be used. + **/ +#define debug_print_buffer(debug_level,data,len) { \ + if (mipv6_debug >= debug_level) { \ + int i; \ + for (i=0; i + * + * $Id$ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include + +#include +#include +#include +#include + +#include "debug.h" +#include "stats.h" +#include "mobhdr.h" +#include "bcache.h" +#include "config.h" + +/** + * mipv6_append_home_addr - Add Home Address Option + * @opt: buffer for Home Address Option + * @offset: offset from beginning of @opt + * @addr: address for HAO + * + * Adds a Home Address Option to a packet. Option is stored in + * @offset from beginning of @opt. The option is created but the + * original source address in IPv6 header is left intact. The source + * address will be changed from home address to CoA after the checksum + * has been calculated in getfrag. Padding is done automatically, and + * @opt must have allocated space for both actual option and pad. + * Returns offset from @opt to end of options. + **/ +int mipv6_append_home_addr(__u8 *opt, int offset, struct in6_addr *addr) +{ + int pad; + struct mipv6_dstopt_homeaddr *ho; + + DEBUG(DBG_DATADUMP, "HAO: %x:%x:%x:%x:%x:%x:%x:%x", + NIPV6ADDR(addr)); + + pad = (6 - offset) & 7; + mipv6_add_pad(opt + offset, pad); + + ho = (struct mipv6_dstopt_homeaddr *)(opt + offset + pad); + ho->type = MIPV6_TLV_HOMEADDR; + ho->length = sizeof(*ho) - 2; + ipv6_addr_copy(&ho->addr, addr); + + return offset + pad + sizeof(*ho); +} +static inline int check_hao_validity(struct mipv6_dstopt_homeaddr *haopt, + u8 *dst1, + struct in6_addr *saddr, + struct in6_addr *daddr) +{ + int addr_type = ipv6_addr_type(&haopt->addr); + struct mipv6_bce bc_entry; + + if (addr_type & IPV6_ADDR_LINKLOCAL || + !(addr_type & IPV6_ADDR_UNICAST)) { + DEBUG(DBG_INFO, "HAO with link local or non-unicast HoA, " + "not sending BE to " + "home address " + "%x:%x:%x:%x:%x:%x:%x:%x ", + "care-of address %x:%x:%x:%x:%x:%x:%x:%x", + NIPV6ADDR(&haopt->addr), + NIPV6ADDR(saddr)); + return -EINVAL; + } else if (dst1[0] != IPPROTO_MOBILITY && + (mipv6_bcache_get(&haopt->addr, + daddr, &bc_entry) != 0 || + ipv6_addr_cmp(saddr, &bc_entry.coa))) { + DEBUG(DBG_INFO, "HAO without binding or incorrect CoA, " + "sending BE code 1: " + "home address %x:%x:%x:%x:%x:%x:%x:%x", + "to care-of address %x:%x:%x:%x:%x:%x:%x:%x", + NIPV6ADDR(&haopt->addr), + NIPV6ADDR(saddr)); + return -ENOENT; + } + return 0; +} +/** + * mipv6_handle_homeaddr - Home Address Destination Option handler + * @skb: packet buffer + * @optoff: offset to where option begins + * + * Handles Home Address Option in IPv6 Destination Option header. + * Packet and offset to option are passed. If HAO is used without + * binding, sends a Binding Error code 1. When sending BE, notify bit + * is cleared to prevent IPv6 error handling from sending ICMP + * Parameter Problem. Returns 1 on success, otherwise zero. + **/ +int mipv6_handle_homeaddr(struct sk_buff *skb, int optoff) +{ + struct in6_addr *saddr = &skb->nh.ipv6h->saddr; + struct in6_addr coaddr; + struct inet6_skb_parm *opt = (struct inet6_skb_parm *) skb->cb; + struct mipv6_dstopt_homeaddr *haopt = + (struct mipv6_dstopt_homeaddr *) &skb->nh.raw[optoff]; + u8 *dst1; + int err; + + DEBUG_FUNC(); + + if (haopt->length != sizeof(*haopt) - 2) { + DEBUG(DBG_WARNING, "HAO has invalid length"); + MIPV6_INC_STATS(n_ha_drop.invalid); + return 0; + } + dst1 = (u8 *)skb->h.raw; + err = check_hao_validity(haopt, dst1, saddr, &skb->nh.ipv6h->daddr); + + if (err) { + haopt->type &= ~(0x80); /* clear notify bit */ + if (err == -ENOENT) + mipv6_send_be(&skb->nh.ipv6h->daddr, saddr, + &haopt->addr, MIPV6_BE_HAO_WO_BINDING); + MIPV6_INC_STATS(n_ha_drop.misc); + return 0; + } + ipv6_addr_copy(&coaddr, saddr); + ipv6_addr_copy(saddr, &haopt->addr); + ipv6_addr_copy(&haopt->addr, &coaddr); + opt->hao = optoff; + if (mip6_fn.mn_check_tunneled_packet != NULL) + mip6_fn.mn_check_tunneled_packet(skb); + + MIPV6_INC_STATS(n_ha_rcvd); + return 1; +} + +/** + * mipv6_icmp_swap_addrs - Switch HAO and src and RT2 and dest for ICMP errors + * @skb: packet buffer + * + * Reset the source address and the Home Address option in skb before + * appending it to an ICMP error message, so original packet appears + * in the error message rather than mangled. + **/ +void mipv6_icmp_swap_addrs(struct sk_buff *skb) +{ + struct inet6_skb_parm *opt = (struct inet6_skb_parm *)skb->cb; + struct in6_addr tmp; + struct in6_addr *hoa; + DEBUG_FUNC(); + if (opt->srcrt2) { + struct rt2_hdr *rt2; + rt2 = (struct rt2_hdr *)(skb->nh.raw + opt->srcrt2); + hoa = &rt2->addr; + + ipv6_addr_copy(&tmp, hoa); + ipv6_addr_copy(hoa, &skb->nh.ipv6h->daddr); + ipv6_addr_copy(&skb->nh.ipv6h->daddr, &tmp); + rt2->rt_hdr.segments_left++; + skb->nh.ipv6h->hop_limit++; + } + if (opt->hao) { + struct mipv6_dstopt_homeaddr *hao; + hao = (struct mipv6_dstopt_homeaddr *)(skb->nh.raw + opt->hao); + hoa = &hao->addr; + + ipv6_addr_copy(&tmp, hoa); + ipv6_addr_copy(hoa, &skb->nh.ipv6h->saddr); + ipv6_addr_copy(&skb->nh.ipv6h->saddr, &tmp); + } +} + +/** + * mipv6_append_rt2hdr - Add Type 2 Routing Header + * @rt: buffer for new routing header + * @addr: intermediate hop address + * + * Adds a Routing Header Type 2 in a packet. Stores newly created + * routing header in buffer @rt. Type 2 RT only carries one address, + * so there is no need to process old routing header. @rt must have + * allocated space for 24 bytes. + **/ +void mipv6_append_rt2hdr(struct ipv6_rt_hdr *rt, struct in6_addr *addr) +{ + struct rt2_hdr *rt2 = (struct rt2_hdr *)rt; + + DEBUG(DBG_DATADUMP, "RT2: %x:%x:%x:%x:%x:%x:%x:%x", + NIPV6ADDR(addr)); + + if (ipv6_addr_type(addr) == IPV6_ADDR_MULTICAST) { + DEBUG(DBG_ERROR, "destination address not unicast"); + return; + } + + memset(rt2, 0, sizeof(*rt2)); + rt2->rt_hdr.type = 2; + rt2->rt_hdr.hdrlen = 2; + rt2->rt_hdr.segments_left = 1; + ipv6_addr_copy(&rt2->addr, addr); +} + +/** + * mipv6_append_dst1opts - Add Destination Option (1) Headers + * @dst1opt: buffer for new destination options + * @saddr: address for Home Address Option + * @old_dst1opt: old destination options + * @len: length of options + * + * Adds Destination Option (1) Header to a packet. New options are + * stored in @dst1opt. If old destination options exist, they are + * copied from @old_dst1opt. Only Home Address Option is destination + * option. @dstopt must have allocated space for @len bytes. @len + * includes Destination Option Header (2 bytes), Home Address Option + * (18 bytes) and possible HAO pad (8n+6). + **/ +/* + * ISSUE: Home Address Destination Option should really be added to a + * new destination option header specified in Mobile IPv6 spec which + * should be placed after routing header(s), but before fragmentation + * header. Putting HAO in DO1 works for now, but support for the new + * placement should be added to the IPv6 stack. + */ +void +mipv6_append_dst1opts(struct ipv6_opt_hdr *dst1opt, struct in6_addr *saddr, + struct ipv6_opt_hdr *old_dst1opt, int len) +{ + int offset; + + if (old_dst1opt) { + memcpy(dst1opt, old_dst1opt, ipv6_optlen(old_dst1opt)); + offset = ipv6_optlen(old_dst1opt); + } else { + offset = sizeof (*dst1opt); + } + dst1opt->hdrlen = (len >> 3) - 1; + mipv6_append_home_addr((__u8 *) dst1opt, offset, saddr); +} + +/** + * mipv6_modify_txoptions - Modify outgoing packets + * @sk: socket + * @skb: packet buffer for outgoing packet + * @old_opt: transmit options + * @fl: packet flow structure + * @dst: pointer to destination cache entry + * + * Adds Home Address Option (for MN packets, when not at home) and + * Routing Header Type 2 (for CN packets when sending to an MN) to + * data packets. Old extension headers are copied from @old_opt (if + * any). Extension headers are _explicitly_ added for packets with + * Mobility Header. Returns the new header structure, or old if no + * changes. + **/ +struct ipv6_txoptions * +mipv6_modify_txoptions(struct sock *sk, struct sk_buff *skb, + struct ipv6_txoptions *old_opt, struct flowi *fl, + struct dst_entry **dst) +{ + struct ipv6_opt_hdr *old_hopopt = NULL; + struct ipv6_opt_hdr *old_dst1opt = NULL; + struct ipv6_rt_hdr *old_srcrt = NULL; + + int srcrtlen = 0, dst1len = 0; + int tot_len, use_hao = 0; + struct ipv6_txoptions *opt; + struct mipv6_bce bc_entry; + struct in6_addr tmpaddr, *saddr, *daddr, coaddr; + __u8 *opt_ptr; + + DEBUG_FUNC(); + + if (fl->proto == IPPROTO_MOBILITY) return old_opt; + /* + * we have to be prepared to the fact that saddr might not be present, + * if that is the case, we acquire saddr just as kernel does. + */ + saddr = fl ? fl->fl6_src : NULL; + daddr = fl ? fl->fl6_dst : NULL; + + if (daddr == NULL) + return old_opt; + if (saddr == NULL) { + int err = ipv6_get_saddr(NULL, daddr, &tmpaddr); + if (err) + return old_opt; + else + saddr = &tmpaddr; + } + + DEBUG(DBG_DATADUMP, + "dest. address of packet: %x:%x:%x:%x:%x:%x:%x:%x", + NIPV6ADDR(daddr)); + DEBUG(DBG_DATADUMP, " and src. address: %x:%x:%x:%x:%x:%x:%x:%x", + NIPV6ADDR(saddr)); + + if (old_opt) { + old_hopopt = old_opt->hopopt; + old_dst1opt = old_opt->dst1opt; + old_srcrt = old_opt->srcrt; + } + + if (mip6_fn.mn_use_hao != NULL) + use_hao = mip6_fn.mn_use_hao(daddr, saddr); + + if (use_hao) { + if (old_dst1opt) + dst1len = ipv6_optlen(old_dst1opt); + dst1len += sizeof(struct mipv6_dstopt_homeaddr) + + ((6 - dst1len) & 7); /* padding */ + } + + if (mipv6_bcache_get(daddr, saddr, &bc_entry) == 0) + srcrtlen = sizeof(struct rt2_hdr); + + if ((tot_len = srcrtlen + dst1len) == 0) { + return old_opt; + } + + tot_len += sizeof(*opt); + + if (!(opt = kmalloc(tot_len, GFP_ATOMIC))) { + return NULL; + } + memset(opt, 0, tot_len); + opt->tot_len = tot_len; + opt_ptr = (__u8 *) (opt + 1); + + if (old_srcrt) { + opt->srcrt = old_srcrt; + opt->opt_nflen += ipv6_optlen(old_srcrt); + } + + if (srcrtlen) { + DEBUG(DBG_DATADUMP, "Binding exists. Adding routing header"); + + opt->srcrt2 = (struct ipv6_rt_hdr *) opt_ptr; + opt->opt_nflen += srcrtlen; + opt_ptr += srcrtlen; + + /* + * Append care-of-address to routing header (original + * destination address is home address, the first + * source route segment gets put to the destination + * address and the home address gets to the last + * segment of source route (just as it should)) + */ + + ipv6_addr_copy(&coaddr, &bc_entry.coa); + + mipv6_append_rt2hdr(opt->srcrt2, &coaddr); + + /* + * reroute output (we have to do this in case of TCP + * segment) unless a routing header of type 0 is also added + */ + if (dst && !opt->srcrt) { + struct in6_addr *tmp = fl->fl6_dst; + fl->fl6_dst = &coaddr; + + dst_release(*dst); + *dst = ip6_route_output(sk, fl); + if (skb) + skb->dst = *dst; + fl->fl6_dst = tmp; + + DEBUG(DBG_DATADUMP, "Rerouted outgoing packet"); + } + } + + /* Only home address option is inserted to first dst opt header */ + if (dst1len) { + opt->dst1opt = (struct ipv6_opt_hdr *) opt_ptr; + opt->opt_flen += dst1len; + opt_ptr += dst1len; + mipv6_append_dst1opts(opt->dst1opt, saddr, + old_dst1opt, dst1len); + opt->mipv6_flags = MIPV6_SND_HAO; + } else if (old_dst1opt) { + opt->dst1opt = old_dst1opt; + opt->opt_flen += ipv6_optlen(old_dst1opt); + } + if (old_hopopt) { + opt->hopopt = old_hopopt; + opt->opt_nflen += ipv6_optlen(old_hopopt); + } + + return opt; +} diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/exthdrs.h linux-2.4.25/net/ipv6/mobile_ip6/exthdrs.h --- linux-2.4.25.old/net/ipv6/mobile_ip6/exthdrs.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.25/net/ipv6/mobile_ip6/exthdrs.h 2004-06-26 11:29:30.000000000 +0100 @@ -0,0 +1,47 @@ +/* + * MIPL Mobile IPv6 Extension Headers header file + * + * $Id$ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _MIPV6_EXTHDRS_H +#define _MIPV6_EXTHDRS_H + +struct in6_addr; +struct sk_buff; +struct ipv6_rt_hdr; +struct ipv6_opt_hdr; +struct ipv6_txoptions; +struct flowi; +struct dst_entry; +/* + * Home Address Destination Option function prototypes + */ +int mipv6_append_home_addr(__u8 *opt, int offset, struct in6_addr *addr); + +int mipv6_handle_homeaddr(struct sk_buff *skb, int optoff); + +void mipv6_icmp_swap_addrs(struct sk_buff *skb); + +/* + * Creates a routing header of type 2. + */ +void mipv6_append_rt2hdr(struct ipv6_rt_hdr *srcrt, struct in6_addr *addr); + +/* Function to add the first destination option header, which may + * include a home address option. + */ +void mipv6_append_dst1opts(struct ipv6_opt_hdr *dst1opt, struct in6_addr *saddr, + struct ipv6_opt_hdr *old_dst1opt, int len); + +struct ipv6_txoptions *mipv6_modify_txoptions( + struct sock *sk, struct sk_buff *skb, + struct ipv6_txoptions *old_opt, struct flowi *fl, + struct dst_entry **dst); + +#endif /* _MIPV6_EXTHDRS_H */ diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/ha.c linux-2.4.25/net/ipv6/mobile_ip6/ha.c --- linux-2.4.25.old/net/ipv6/mobile_ip6/ha.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.25/net/ipv6/mobile_ip6/ha.c 2004-06-26 11:29:30.000000000 +0100 @@ -0,0 +1,553 @@ +/* + * Home-agent functionality + * + * Authors: + * Sami Kivisaari + * Henrik Petander + * + * $Id$ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Changes: Venkata Jagana, + * Krishna Kumar : Statistics fix + * Masahide Nakamura : Use of mipv6_forward + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_SYSCTL +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "tunnel_ha.h" +#include "bcache.h" +#include "stats.h" +#include "debug.h" +#include "util.h" +#include "ha.h" +#include "config.h" +#include "mobhdr.h" + +static int mipv6_ha_tunnel_sitelocal = 0; + +#ifdef CONFIG_SYSCTL + +static struct ctl_table_header *mipv6_ha_sysctl_header; + +static struct mipv6_ha_sysctl_table +{ + struct ctl_table_header *sysctl_header; + ctl_table mipv6_vars[3]; + ctl_table mipv6_mobility_table[2]; + ctl_table mipv6_proto_table[2]; + ctl_table mipv6_root_table[2]; +} mipv6_ha_sysctl = { + NULL, + + {{NET_IPV6_MOBILITY_TUNNEL_SITELOCAL, "tunnel_sitelocal", + &mipv6_ha_tunnel_sitelocal, sizeof(int), 0644, NULL, + &proc_dointvec}, + {0}}, + + {{NET_IPV6_MOBILITY, "mobility", NULL, 0, 0555, + mipv6_ha_sysctl.mipv6_vars}, {0}}, + {{NET_IPV6, "ipv6", NULL, 0, 0555, + mipv6_ha_sysctl.mipv6_mobility_table}, {0}}, + {{CTL_NET, "net", NULL, 0, 0555, + mipv6_ha_sysctl.mipv6_proto_table}, {0}} +}; + +#endif /* CONFIG_SYSCTL */ + + +/* this is defined in kernel IPv6 module (sockglue.c) */ +extern struct packet_type ipv6_packet_type; + +/* mipv6_forward: Intercept NS packets destined to home address of MN */ +int mipv6_forward(struct sk_buff *skb) +{ + struct ipv6hdr *ipv6h; + struct in6_addr *daddr, *saddr; + __u8 nexthdr; + int nhoff; + + if (skb == NULL) return 0; + + ipv6h = skb->nh.ipv6h; + daddr = &ipv6h->daddr; + saddr = &ipv6h->saddr; + + nexthdr = ipv6h->nexthdr; + nhoff = sizeof(*ipv6h); + + if (ipv6_ext_hdr(nexthdr)) + nhoff = ipv6_skip_exthdr(skb, nhoff, &nexthdr, + skb->len - sizeof(*ipv6h)); + + /* Do not to forward Neighbor Solicitation to Home Address of MN */ + if (nexthdr == IPPROTO_ICMPV6) { + struct icmp6hdr *icmp6h; + int dest_type; + + if (nhoff < 0 || !pskb_may_pull(skb, nhoff + + sizeof(struct icmp6hdr))) { + kfree_skb(skb); + return 0; + } + + dest_type = ipv6_addr_type(daddr); + icmp6h = (struct icmp6hdr *)&skb->nh.raw[nhoff]; + + /* Intercepts NS to HoA of MN */ + + if ((icmp6h->icmp6_type == NDISC_NEIGHBOUR_SOLICITATION) || + ((dest_type & IPV6_ADDR_MULTICAST) && + (icmp6h->icmp6_type == NDISC_ROUTER_ADVERTISEMENT))) { + ip6_input(skb); + } else { + ip6_forward(skb); + } + } else { + ip6_forward(skb); + } + return 0; +} + + +/** + * mipv6_proxy_nd_rem - stop acting as a proxy for @home_address + * @home_addr: address to remove + * @ha_addr: home agent's address on home link + * @linklocal: link-local compatibility bit + * + * When Home Agent acts as a proxy for an address it must leave the + * solicited node multicast group for that address and stop responding + * to neighbour solicitations. + **/ +static int mipv6_proxy_nd_rem(struct in6_addr *home_addr, + int ifindex, int linklocal) +{ + /* When MN returns home HA leaves the solicited mcast groups + * for MNs home addresses + */ + int err; + struct net_device *dev; + + DEBUG_FUNC(); + + if ((dev = dev_get_by_index(ifindex)) == NULL) { + DEBUG(DBG_ERROR, "couldn't get dev"); + return -ENODEV; + } +#if 1 /* TEST */ + /* Remove link-local entry */ + if (linklocal) { + struct in6_addr ll_addr; + mipv6_generate_ll_addr(&ll_addr, home_addr); + if ((err = pneigh_delete(&nd_tbl, &ll_addr, dev)) < 0) { + DEBUG(DBG_INFO, + "peigh_delete failed for " + "%x:%x:%x:%x:%x:%x:%x:%x", + NIPV6ADDR(&ll_addr)); + } + } +#endif + /* Remove global (or site-local) entry */ + if ((err = pneigh_delete(&nd_tbl, home_addr, dev)) < 0) { + DEBUG(DBG_INFO, + "peigh_delete failed for " + "%x:%x:%x:%x:%x:%x:%x:%x", + NIPV6ADDR(home_addr)); + } + dev_put(dev); + return err; +} + +/** + * mipv6_proxy_nd - join multicast group for this address + * @home_addr: address to defend + * @ha_addr: home agent's address on home link + * @linklocal: link-local compatibility bit + * + * While Mobile Node is away from home, Home Agent acts as a proxy for + * @home_address. HA responds to neighbour solicitations for @home_address + * thus getting all packets destined to home address of MN. + **/ +static int mipv6_proxy_nd(struct in6_addr *home_addr, + int ifindex, int linklocal) +{ + /* The HA sends a proxy ndisc_na message to all hosts on MN's + * home subnet by sending a neighbor advertisement with the + * home address or all addresses of the mobile node if the + * prefix is not 0. The addresses are formed by combining the + * suffix or the host part of the address with each subnet + * prefix that exists in the home subnet + */ + + /* Since no previous entry for MN exists a proxy_nd advertisement + * is sent to all nodes link local multicast address + */ + int err = -1; + + struct net_device *dev; + struct in6_addr na_saddr; + struct in6_addr ll_addr; + struct pneigh_entry *ll_pneigh; + struct in6_addr mcdest; + int send_ll_na = 0; + int inc_opt = 1; + int solicited = 0; + int override = 1; + + DEBUG_FUNC(); + + if ((dev = dev_get_by_index(ifindex)) == NULL) { + DEBUG(DBG_ERROR, "couldn't get dev"); + return -ENODEV; + } + + if (!pneigh_lookup(&nd_tbl, home_addr, dev, 1)) { + DEBUG(DBG_INFO, + "peigh_lookup failed for " + "%x:%x:%x:%x:%x:%x:%x:%x", + NIPV6ADDR(home_addr)); + goto free_dev; + } +#if 1 /* TEST */ + if (linklocal) { + mipv6_generate_ll_addr(&ll_addr, home_addr); + + if ((ll_pneigh = pneigh_lookup(&nd_tbl, &ll_addr, + dev, 1)) == NULL) { + DEBUG(DBG_INFO, + "peigh_lookup failed for " + "%x:%x:%x:%x:%x:%x:%x:%x", + NIPV6ADDR(&ll_addr)); + pneigh_delete(&nd_tbl, home_addr, dev); + goto free_dev; + } else { + send_ll_na = 1; + } + } else { + ll_pneigh = NULL; + } +#endif + /* Proxy neighbor advertisement of MN's home address + * to all nodes solicited multicast address + */ + if (!ipv6_get_lladdr(dev, &na_saddr)) { + ipv6_addr_all_nodes(&mcdest); + ndisc_send_na(dev, NULL, &mcdest, home_addr, 0, + solicited, override, inc_opt); +#if 1 /* TEST */ + if (send_ll_na) { + ndisc_send_na(dev, NULL, &mcdest, &ll_addr, + 0, solicited, override, inc_opt); + } +#endif + err = 0; + } else { + DEBUG(DBG_ERROR, "failed to get link local address for sending proxy NA"); + } +free_dev: + dev_put(dev); + return err; + +} + +struct inet6_ifaddr *is_on_link_ipv6_address(struct in6_addr *mn_haddr, + struct in6_addr *ha_addr) +{ + struct inet6_ifaddr *ifp; + struct inet6_dev *in6_dev; + struct inet6_ifaddr *oifp = NULL; + + if ((ifp = ipv6_get_ifaddr(ha_addr, 0)) == NULL) + return NULL; + + if ((in6_dev = ifp->idev) != NULL) { + in6_dev_hold(in6_dev); + oifp = in6_dev->addr_list; + while (oifp != NULL) { + spin_lock(&oifp->lock); + if (mipv6_prefix_compare(&oifp->addr, mn_haddr, + oifp->prefix_len) && + !(oifp->flags & IFA_F_TENTATIVE)) { + spin_unlock(&oifp->lock); + DEBUG(DBG_INFO, "Home Addr Opt: on-link"); + in6_ifa_hold(oifp); + break; + } + spin_unlock(&oifp->lock); + oifp = oifp->if_next; + } + in6_dev_put(in6_dev); + } + in6_ifa_put(ifp); +/* DEBUG(DBG_WARNING, "Home Addr Opt NOT on-link"); */ + return oifp; + +} + +/* + * Lifetime checks. ifp->valid_lft >= ifp->prefered_lft always (see addrconf.c) + * Returned value is in seconds. + */ + +static __u32 get_min_lifetime(struct inet6_ifaddr *ifp, __u32 lifetime) +{ + __u32 rem_lifetime = 0; + unsigned long now = jiffies; + + if (ifp->valid_lft == 0) { + rem_lifetime = lifetime; + } else { + __u32 valid_lft_left = + ifp->valid_lft - ((now - ifp->tstamp) / HZ); + rem_lifetime = + min_t(unsigned long, valid_lft_left, lifetime); + } + + return rem_lifetime; +} + +#define MAX_LIFETIME 1000 + +/** + * mipv6_lifetime_check - check maximum lifetime is not exceeded + * @lifetime: lifetime to check + * + * Checks @lifetime does not exceed %MAX_LIFETIME. Returns @lifetime + * if not exceeded, otherwise returns %MAX_LIFETIME. + **/ +static int mipv6_lifetime_check(int lifetime) +{ + return (lifetime > MAX_LIFETIME) ? MAX_LIFETIME : lifetime; +} + +/* Generic routine handling finish of BU processing */ +void mipv6_bu_finish(struct inet6_ifaddr *ifp, int ifindex, __u8 ba_status, + struct in6_addr *daddr, struct in6_addr *haddr, + struct in6_addr *coa, struct in6_addr *rep_coa, + __u32 ba_lifetime, __u16 sequence, __u8 flags, __u8 *k_bu) +{ + int err; + + if (ba_status >= REASON_UNSPECIFIED) { + /* DAD failed */ + goto out; + } + + ba_lifetime = get_min_lifetime(ifp, ba_lifetime); + ba_lifetime = mipv6_lifetime_check(ba_lifetime); + + if ((err = mipv6_bcache_add(ifindex, daddr, haddr, coa, + ba_lifetime, sequence, flags, + HOME_REGISTRATION)) != 0 ) { + DEBUG(DBG_WARNING, "home reg failed."); + + if (err == -ENOMEDIUM) + return; + + ba_status = INSUFFICIENT_RESOURCES; + } else { + DEBUG(DBG_INFO, "home reg succeeded."); + } + + DEBUG(DBG_DATADUMP, "home_addr: %x:%x:%x:%x:%x:%x:%x:%x", + NIPV6ADDR(haddr)); + DEBUG(DBG_DATADUMP, "coa: %x:%x:%x:%x:%x:%x:%x:%x", + NIPV6ADDR(coa)); + DEBUG(DBG_DATADUMP, "lifet:%d, seq:%d", ba_lifetime, sequence); +out: + mipv6_send_ba(daddr, haddr, coa, rep_coa, ba_status, sequence, + ba_lifetime, k_bu); +} + +static int ha_proxy_create(int flags, int ifindex, struct in6_addr *coa, + struct in6_addr *our_addr, struct in6_addr *home_addr) +{ + int ret; + + if ((ret = mipv6_add_tnl_to_mn(coa, our_addr, home_addr)) <= 0) { + if (ret != -ENOMEDIUM) { + DEBUG(DBG_ERROR, "unable to configure tunnel to MN!"); + } + return -1; + } + if (mipv6_proxy_nd(home_addr, ifindex, + flags & MIPV6_BU_F_LLADDR) != 0) { + DEBUG(DBG_ERROR, "mipv6_proxy_nd failed!"); + mipv6_del_tnl_to_mn(coa, our_addr, home_addr); + return -2; + } + return 0; +} + +static void ha_proxy_del(struct in6_addr *home_addr, struct mipv6_bce *entry) +{ + if (mipv6_proxy_nd_rem(&entry->home_addr, entry->ifindex, + entry->flags & MIPV6_BU_F_LLADDR) == 0) { + DEBUG(DBG_INFO, "proxy_nd succ"); + } else { + DEBUG(DBG_INFO, "proxy_nd fail"); + } + mipv6_del_tnl_to_mn(&entry->coa, &entry->our_addr, home_addr); +} + +static void bc_home_add(int ifindex, + struct in6_addr *daddr, struct in6_addr *haddr, + struct in6_addr *coa, struct in6_addr *rep_coa, + __u32 lifetime, __u16 sequence, __u8 flags, + __u8 *k_bu) +{ + struct inet6_ifaddr *ifp = NULL; + __u8 ba_status = SUCCESS; + + DEBUG_FUNC(); + + ifp = is_on_link_ipv6_address(haddr, daddr); + + if (ifp == NULL) { + ba_status = NOT_HOME_SUBNET; + } else if (((ipv6_addr_type(haddr) & IPV6_ADDR_SITELOCAL) || + (ipv6_addr_type(coa) & IPV6_ADDR_SITELOCAL)) + && !mipv6_ha_tunnel_sitelocal) { + /* Site-local home or care-of addresses are not + accepted by default */ + ba_status = ADMINISTRATIVELY_PROHIBITED; + } else { + int ret; + + ifindex = ifp->idev->dev->ifindex; + + if ((ret = mipv6_dad_start(ifp, ifindex, daddr, + haddr, coa, rep_coa, lifetime, + sequence, flags)) < 0) { + /* An error occurred */ + ba_status = -ret; + } else if (ret) { + /* DAD is needed to be performed. */ + in6_ifa_put(ifp); + return; + } + } + + mipv6_bu_finish(ifp, ifindex, ba_status, daddr, haddr, coa, + rep_coa, lifetime, sequence, flags, k_bu); + if (ifp) + in6_ifa_put(ifp); +} + +static void bc_home_delete(struct in6_addr *daddr, struct in6_addr *haddr, + struct in6_addr *coa, struct in6_addr *rep_coa, + __u16 sequence, __u8 flags, __u8 *k_bu) +{ + __u8 status = SUCCESS; + struct mipv6_bce bce; + + /* Primary Care-of Address Deregistration */ + if (mipv6_bcache_get(haddr, daddr, &bce) < 0) { + DEBUG(DBG_INFO, "entry is not in cache"); + status = NOT_HA_FOR_MN; + } else { + ha_proxy_del(&bce.home_addr, &bce); + mipv6_bcache_delete(haddr, daddr, HOME_REGISTRATION); + } + mipv6_send_ba(daddr, haddr, coa, rep_coa, status, sequence, 0, k_bu); +} + +extern int mipv6_ra_rcv_ptr(struct sk_buff *skb, struct icmp6hdr *msg); + + +static int +mipv6_ha_tnl_xmit_stats_hook(struct ip6_tnl *t, struct sk_buff *skb) +{ + DEBUG_FUNC(); + if (is_mip6_tnl(t)) + MIPV6_INC_STATS(n_encapsulations); + return IP6_TNL_ACCEPT; +} + +static struct ip6_tnl_hook_ops mipv6_ha_tnl_xmit_stats_ops = { + {NULL, NULL}, + IP6_TNL_PRE_ENCAP, + IP6_TNL_PRI_LAST, + mipv6_ha_tnl_xmit_stats_hook +}; + +static int +mipv6_ha_tnl_rcv_stats_hook(struct ip6_tnl *t, struct sk_buff *skb) +{ + DEBUG_FUNC(); + if (is_mip6_tnl(t)) + MIPV6_INC_STATS(n_decapsulations); + return IP6_TNL_ACCEPT; +} + +static struct ip6_tnl_hook_ops mipv6_ha_tnl_rcv_stats_ops = { + {NULL, NULL}, + IP6_TNL_PRE_DECAP, + IP6_TNL_PRI_LAST, + mipv6_ha_tnl_rcv_stats_hook +}; + +static struct mip6_func old; + +int __init mipv6_ha_init(void) +{ + DEBUG_FUNC(); + +#ifdef CONFIG_SYSCTL + if (!(mipv6_ha_sysctl_header = + register_sysctl_table(mipv6_ha_sysctl.mipv6_root_table, 0))) + printk(KERN_ERR "Failed to register sysctl handlers!"); +#endif + memcpy(&old, &mip6_fn, sizeof(struct mip6_func)); + mip6_fn.bce_home_add = bc_home_add; + mip6_fn.bce_home_del = bc_home_delete; + mip6_fn.proxy_del = ha_proxy_del; + mip6_fn.proxy_create = ha_proxy_create; + /* register packet interception hooks */ + ip6ip6_tnl_register_hook(&mipv6_ha_tnl_xmit_stats_ops); + ip6ip6_tnl_register_hook(&mipv6_ha_tnl_rcv_stats_ops); + return 0; +} + +void __exit mipv6_ha_exit(void) +{ + DEBUG_FUNC(); + +#ifdef CONFIG_SYSCTL + unregister_sysctl_table(mipv6_ha_sysctl_header); +#endif + + /* remove packet interception hooks */ + ip6ip6_tnl_unregister_hook(&mipv6_ha_tnl_rcv_stats_ops); + ip6ip6_tnl_unregister_hook(&mipv6_ha_tnl_xmit_stats_ops); + + mip6_fn.bce_home_add = old.bce_home_add; + mip6_fn.bce_home_del = old.bce_home_del; + mip6_fn.proxy_del = old.proxy_del; + mip6_fn.proxy_create = old.proxy_create; +} diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/ha.h linux-2.4.25/net/ipv6/mobile_ip6/ha.h --- linux-2.4.25.old/net/ipv6/mobile_ip6/ha.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.25/net/ipv6/mobile_ip6/ha.h 2004-06-26 11:29:30.000000000 +0100 @@ -0,0 +1,39 @@ +/* + * MIPL Mobile IPv6 Home Agent header file + * + * $Id$ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _HA_H +#define _HA_H + +int mipv6_ha_init(void); +void mipv6_ha_exit(void); + +int mipv6_dad_start(struct inet6_ifaddr *ifp, int ifindex, + struct in6_addr *daddr, struct in6_addr *haddr, + struct in6_addr *coa, struct in6_addr *rep_coa, + __u32 ba_lifetime, __u16 sequence, __u8 flags); + +void mipv6_bu_finish(struct inet6_ifaddr *ifp, int ifindex, + __u8 ba_status, struct in6_addr *daddr, + struct in6_addr *haddr, struct in6_addr *coa, + struct in6_addr *rep_coa, __u32 ba_lifetime, + __u16 sequence, __u8 flags, __u8 *k_bu); + + +static __inline__ void mipv6_generate_ll_addr(struct in6_addr *ll_addr, + struct in6_addr *addr) +{ + ll_addr->s6_addr32[0] = htonl(0xfe800000); + ll_addr->s6_addr32[1] = 0; + ll_addr->s6_addr32[2] = addr->s6_addr32[2]; + ll_addr->s6_addr32[3] = addr->s6_addr32[3]; +} + +#endif diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/halist.c linux-2.4.25/net/ipv6/mobile_ip6/halist.c --- linux-2.4.25.old/net/ipv6/mobile_ip6/halist.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.25/net/ipv6/mobile_ip6/halist.c 2004-06-26 11:29:30.000000000 +0100 @@ -0,0 +1,507 @@ +/* + * Home Agents List + * + * Authors: + * Antti Tuominen + * + * $Id$ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + */ + +#define PREF_BASE 0xffff /* MAX value for u16 field in RA */ + +#include +#include +#include +#include +#include +#include +#include + +#include "hashlist.h" +#include "util.h" +#include "debug.h" + +struct mipv6_halist { + struct hashlist *entries; + struct timer_list expire_timer; +}; + +static rwlock_t home_agents_lock = RW_LOCK_UNLOCKED; + +static struct mipv6_halist home_agents; + +struct mipv6_halist_entry { + struct hashlist_entry e; + int ifindex; /* Link identifier */ + struct in6_addr link_local_addr; /* HA's link-local address */ + struct in6_addr global_addr; /* HA's Global address */ + int plen; + long preference; /* The preference for this HA */ + unsigned long expire; /* expiration time (jiffies) */ +}; + +static inline void mipv6_ha_ac_add(struct in6_addr *ll_addr, int ifindex, + struct in6_addr *glob_addr, int plen) +{ + struct net_device *dev; + + if ((dev = __dev_get_by_index(ifindex)) && ipv6_chk_addr(ll_addr, dev)) { + struct in6_addr addr; + mipv6_ha_anycast(&addr, glob_addr, plen); + ipv6_dev_ac_inc(dev, &addr); + } +} + +static inline void mipv6_ha_ac_del(struct in6_addr *ll_addr, int ifindex, + struct in6_addr *glob_addr, int plen) +{ + struct net_device *dev; + + if ((dev = __dev_get_by_index(ifindex)) && ipv6_chk_addr(ll_addr, dev)) { + struct in6_addr addr; + mipv6_ha_anycast(&addr, glob_addr, plen); + ipv6_dev_ac_dec(dev, &addr); + } +} + +struct preflist_iterator_args { + int count; + int requested; + int ifindex; + struct in6_addr *list; +}; + +static int preflist_iterator(void *data, void *args, + unsigned long *pref) +{ + struct preflist_iterator_args *state = + (struct preflist_iterator_args *)args; + struct mipv6_halist_entry *entry = + (struct mipv6_halist_entry *)data; + struct in6_addr *newaddr = + (struct in6_addr *)state->list + state->count; + + if (state->count >= state->requested) + return ITERATOR_STOP; + + if (time_after(jiffies, entry->expire)) { + if (!ipv6_addr_any(&entry->link_local_addr)) { + mipv6_ha_ac_del(&entry->link_local_addr, + entry->ifindex, + &entry->global_addr, entry->plen); + } + DEBUG(DBG_INFO, "preflist_iterator: Deleting entry with address %x:%x:%x:%x:%x:%x:%x:%x to list", NIPV6ADDR(&entry->global_addr)); + return ITERATOR_DELETE_ENTRY; + } + if (state->ifindex != entry->ifindex) + return ITERATOR_CONT; + + ipv6_addr_copy(newaddr, &entry->global_addr); + DEBUG(DBG_INFO, "preflist_iterator: adding new entry with address %x:%x:%x:%x:%x:%x:%x:%x to list", NIPV6ADDR(&entry->global_addr)); + state->count++; + + return ITERATOR_CONT; +} + +static int gc_iterator(void *data, void *args, + unsigned long *pref) +{ + struct mipv6_halist_entry *entry = + (struct mipv6_halist_entry *)data; + + int *type = (int *)args; + + if (*type == 1 || time_after(jiffies, entry->expire)) { + if (!ipv6_addr_any(&entry->link_local_addr)) { + mipv6_ha_ac_del(&entry->link_local_addr, + entry->ifindex, + &entry->global_addr, entry->plen); + } + return ITERATOR_DELETE_ENTRY; + } + + return ITERATOR_CONT; +} + +static int mipv6_halist_gc(int type) +{ + DEBUG_FUNC(); + hashlist_iterate(home_agents.entries, &type, gc_iterator); + return 0; +} + +static void mipv6_halist_expire(unsigned long dummy) +{ + DEBUG_FUNC(); + + write_lock(&home_agents_lock); + mipv6_halist_gc(0); + write_unlock(&home_agents_lock); +} + + +static struct mipv6_halist_entry *mipv6_halist_new_entry(void) +{ + struct mipv6_halist_entry *entry; + + DEBUG_FUNC(); + + entry = hashlist_alloc(home_agents.entries, SLAB_ATOMIC); + + return entry; +} + + + +/** + * mipv6_halist_add - Add new home agent to the Home Agents List + * @ifindex: interface identifier + * @glob_addr: home agent's global address + * @ll_addr: home agent's link-local address + * @pref: relative preference for this home agent + * @lifetime: lifetime for the entry + * + * Adds new home agent to the Home Agents List. The list is interface + * specific and @ifindex tells through which interface the home agent + * was heard. Returns zero on success and negative on failure. + **/ + +int mipv6_halist_add(int ifindex, struct in6_addr *glob_addr, int plen, + struct in6_addr *ll_addr, unsigned int pref, __u32 lifetime) +{ + int update = 0, ret = 0; + unsigned int mpref; + struct mipv6_halist_entry *entry = NULL; + + DEBUG_FUNC(); + + write_lock(&home_agents_lock); + + if (glob_addr == NULL || lifetime <= 0) { + DEBUG(DBG_WARNING, "invalid arguments"); + ret = -EINVAL; + goto out; + } + mpref = PREF_BASE - pref; + if ((entry = (struct mipv6_halist_entry *) + hashlist_get(home_agents.entries, glob_addr)) != NULL) { + if (entry->ifindex == ifindex) { + DEBUG(DBG_DATADUMP, "updating old entry with address %x:%x:%x:%x:%x:%x:%x:%x", NIPV6ADDR(glob_addr)); + update = 1; + } else { + DEBUG(DBG_INFO, "halist_add : adding new entry with address %x:%x:%x:%x:%x:%x:%x:%x", NIPV6ADDR(glob_addr)); + update = 0; + } + } + if (update) { + entry->expire = jiffies + lifetime * HZ; + if (entry->preference != mpref) { + entry->preference = mpref; + ret = hashlist_reposition(home_agents.entries, + (void *)entry, mpref); + } + } else { + entry = mipv6_halist_new_entry(); + if (entry == NULL) { + DEBUG(DBG_INFO, "list full"); + ret = -ENOMEM; + goto out; + } + entry->ifindex = ifindex; + if (ll_addr) { + ipv6_addr_copy(&entry->link_local_addr, ll_addr); + mipv6_ha_ac_add(ll_addr, ifindex, glob_addr, plen); + } else + ipv6_addr_set(&entry->link_local_addr, 0, 0, 0, 0); + + ipv6_addr_copy(&entry->global_addr, glob_addr); + entry->plen = plen; + entry->preference = mpref; + entry->expire = jiffies + lifetime * HZ; + ret = hashlist_add(home_agents.entries, glob_addr, mpref, + entry); + } +out: + write_unlock(&home_agents_lock); + return ret; +} + +/** + * mipv6_halist_delete - delete home agent from Home Agents List + * @glob_addr: home agent's global address + * + * Deletes entry for home agent @glob_addr from the Home Agent List. + **/ +int mipv6_halist_delete(struct in6_addr *glob_addr) +{ + struct hashlist_entry *e; + struct mipv6_halist_entry *entry; + DEBUG_FUNC(); + + if (glob_addr == NULL) { + DEBUG(DBG_WARNING, "invalid glob addr"); + return -EINVAL; + } + write_lock(&home_agents_lock); + if ((e = hashlist_get(home_agents.entries, glob_addr)) == NULL) { + write_unlock(&home_agents_lock); + return -ENOENT; + } + hashlist_delete(home_agents.entries, e); + entry = (struct mipv6_halist_entry *)e; + if (!ipv6_addr_any(&entry->link_local_addr)) { + mipv6_ha_ac_del(&entry->link_local_addr, entry->ifindex, + &entry->global_addr, entry->plen); + } + hashlist_free(home_agents.entries, e); + write_unlock(&home_agents_lock); + return 0; +} + +/** + * mipv6_ha_get_pref_list - Get list of preferred home agents + * @ifindex: interface identifier + * @addrs: pointer to a buffer to store the list + * @max: maximum number of home agents to return + * + * Creates a list of @max preferred (or all known if less than @max) + * home agents. Home Agents List is interface specific so you must + * supply @ifindex. Stores list in addrs and returns number of home + * agents stored. On failure, returns a negative value. + **/ +int mipv6_ha_get_pref_list(int ifindex, struct in6_addr **addrs, int max) +{ + struct preflist_iterator_args args; + + DEBUG_FUNC(); + if (max <= 0) { + *addrs = NULL; + return 0; + } + + args.count = 0; + args.requested = max; + args.ifindex = ifindex; + args.list = kmalloc(max * sizeof(struct in6_addr), GFP_ATOMIC); + + if (args.list == NULL) return -ENOMEM; + + read_lock(&home_agents_lock); + hashlist_iterate(home_agents.entries, &args, preflist_iterator); + read_unlock(&home_agents_lock); + + if (args.count >= 0) { + *addrs = args.list; + } else { + kfree(args.list); + *addrs = NULL; + } + + return args.count; +} + +struct getaddr_iterator_args { + struct net_device *dev; + struct in6_addr *addr; +}; + +static int getaddr_iterator(void *data, void *args, + unsigned long *pref) +{ + struct mipv6_halist_entry *entry = + (struct mipv6_halist_entry *)data; + struct getaddr_iterator_args *state = + (struct getaddr_iterator_args *)args; + + if (entry->ifindex != state->dev->ifindex) + return ITERATOR_CONT; + + if (ipv6_chk_addr(&entry->global_addr, state->dev)) { + ipv6_addr_copy(state->addr, &entry->global_addr); + return ITERATOR_STOP; + } + return ITERATOR_CONT; +} + +/* + * Get Home Agent Address for given interface. If node is not serving + * as a HA for this interface returns negative error value. + */ +int mipv6_ha_get_addr(int ifindex, struct in6_addr *addr) +{ + struct getaddr_iterator_args args; + struct net_device *dev; + + if (ifindex <= 0) + return -EINVAL; + + if ((dev = dev_get_by_index(ifindex)) == NULL) + return -ENODEV; + + memset(addr, 0, sizeof(struct in6_addr)); + args.dev = dev; + args.addr = addr; + read_lock(&home_agents_lock); + hashlist_iterate(home_agents.entries, &args, getaddr_iterator); + read_unlock(&home_agents_lock); + dev_put(dev); + + if (ipv6_addr_any(addr)) + return -ENOENT; + + return 0; +} + +#define HALIST_INFO_LEN 81 + +struct procinfo_iterator_args { + char *buffer; + int offset; + int length; + int skip; + int len; +}; + +static int procinfo_iterator(void *data, void *args, + unsigned long *pref) +{ + struct procinfo_iterator_args *arg = + (struct procinfo_iterator_args *)args; + struct mipv6_halist_entry *entry = + (struct mipv6_halist_entry *)data; + unsigned long int expire; + + DEBUG_FUNC(); + + if (entry == NULL) return ITERATOR_ERR; + + if (time_after(jiffies, entry->expire)) { + if (!ipv6_addr_any(&entry->link_local_addr)) { + mipv6_ha_ac_del(&entry->link_local_addr, + entry->ifindex, + &entry->global_addr, entry->plen); + } + return ITERATOR_DELETE_ENTRY; + } + if (arg->skip < arg->offset / HALIST_INFO_LEN) { + arg->skip++; + return ITERATOR_CONT; + } + + if (arg->len >= arg->length) + return ITERATOR_CONT; + + expire = (entry->expire - jiffies) / HZ; + + arg->len += sprintf(arg->buffer + arg->len, + "%02d %08x%08x%08x%08x %08x%08x%08x%08x %05ld %05ld\n", + entry->ifindex, + ntohl(entry->global_addr.s6_addr32[0]), + ntohl(entry->global_addr.s6_addr32[1]), + ntohl(entry->global_addr.s6_addr32[2]), + ntohl(entry->global_addr.s6_addr32[3]), + ntohl(entry->link_local_addr.s6_addr32[0]), + ntohl(entry->link_local_addr.s6_addr32[1]), + ntohl(entry->link_local_addr.s6_addr32[2]), + ntohl(entry->link_local_addr.s6_addr32[3]), + -(entry->preference - PREF_BASE), expire); + + return ITERATOR_CONT; +} + +static int halist_proc_info(char *buffer, char **start, off_t offset, + int length) +{ + struct procinfo_iterator_args args; + + DEBUG_FUNC(); + + args.buffer = buffer; + args.offset = offset; + args.length = length; + args.skip = 0; + args.len = 0; + + read_lock_bh(&home_agents_lock); + hashlist_iterate(home_agents.entries, &args, procinfo_iterator); + read_unlock_bh(&home_agents_lock); + + *start = buffer; + if (offset) + *start += offset % HALIST_INFO_LEN; + + args.len -= offset % HALIST_INFO_LEN; + + if (args.len > length) + args.len = length; + if (args.len < 0) + args.len = 0; + + return args.len; +} + +static int halist_compare(void *data, void *hashkey) +{ + struct mipv6_halist_entry *e = (struct mipv6_halist_entry *)data; + struct in6_addr *key = (struct in6_addr *)hashkey; + + return ipv6_addr_cmp(&e->global_addr, key); +} + +static __u32 halist_hash(void *hashkey) +{ + struct in6_addr *key = (struct in6_addr *)hashkey; + __u32 hash; + + hash = key->s6_addr32[0] ^ + key->s6_addr32[1] ^ + key->s6_addr32[2] ^ + key->s6_addr32[3]; + + return hash; +} + +int __init mipv6_halist_init(__u32 size) +{ + DEBUG_FUNC(); + + if (size <= 0) { + DEBUG(DBG_ERROR, "size must be at least 1"); + return -EINVAL; + } + init_timer(&home_agents.expire_timer); + home_agents.expire_timer.data = 0; + home_agents.expire_timer.function = mipv6_halist_expire; + home_agents_lock = RW_LOCK_UNLOCKED; + + home_agents.entries = hashlist_create(16, size, sizeof(struct mipv6_halist_entry), + "mip6_halist", NULL, NULL, + halist_compare, halist_hash); + + if (home_agents.entries == NULL) { + DEBUG(DBG_ERROR, "Failed to initialize hashlist"); + return -ENOMEM; + } + + proc_net_create("mip6_home_agents", 0, halist_proc_info); + DEBUG(DBG_INFO, "Home Agents List initialized"); + return 0; +} + +void __exit mipv6_halist_exit(void) +{ + DEBUG_FUNC(); + proc_net_remove("mip6_home_agents"); + write_lock_bh(&home_agents_lock); + DEBUG(DBG_INFO, "Stopping the halist timer"); + del_timer(&home_agents.expire_timer); + mipv6_halist_gc(1); + write_unlock_bh(&home_agents_lock); + hashlist_destroy(home_agents.entries); +} diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/halist.h linux-2.4.25/net/ipv6/mobile_ip6/halist.h --- linux-2.4.25.old/net/ipv6/mobile_ip6/halist.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.25/net/ipv6/mobile_ip6/halist.h 2004-06-26 11:29:30.000000000 +0100 @@ -0,0 +1,28 @@ +/* + * MIPL Mobile IPv6 Home Agents List header file + * + * $Id$ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _HALIST_H +#define _HALIST_H + +int mipv6_halist_init(__u32 size); + +void mipv6_halist_exit(void); + +int mipv6_halist_add(int ifindex, struct in6_addr *glob_addr, int plen, + struct in6_addr *ll_addr, unsigned int pref, __u32 lifetime); + +int mipv6_halist_delete(struct in6_addr *glob_addr); + +int mipv6_ha_get_pref_list(int ifindex, struct in6_addr **addrs, int max); + +int mipv6_ha_get_addr(int ifindex, struct in6_addr *addr); + +#endif /* _HALIST_H */ diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/hashlist.c linux-2.4.25/net/ipv6/mobile_ip6/hashlist.c --- linux-2.4.25.old/net/ipv6/mobile_ip6/hashlist.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.25/net/ipv6/mobile_ip6/hashlist.c 2004-06-26 11:29:31.000000000 +0100 @@ -0,0 +1,351 @@ +/* + * Generic hashtable with chaining. Supports secodary sort order + * with doubly linked-list. + * + * Authors: + * Sami Kivisaari + * Antti Tuominen + * + * $Id: s.hashlist.c 1.21 02/10/07 19:31:52+03:00 antti@traci.mipl.mediapoli.com $ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ + +#include +#include "hashlist.h" +#include "debug.h" + +struct hashlist { + int count; /* entry count */ + int maxcount; /* max entries */ + __u32 bucketnum; /* hash buckets */ + + kmem_cache_t *kmem; + + struct list_head *hashtable; + struct list_head sortedlist; + + int (*compare)(void *data, void *hashkey); + __u32 (*hash_function)(void *hashkey); +}; + +/** + * hashlist_create - Create new hashlist + * @bucketnum: number of hash buckets + * @maxentries: maximum number of entries (0 = no limit) + * @size: entry size in bytes + * @name: name for kmem_cache_t + * @ctor: kmem_cache_t constructor + * @dtor: kmem_cache_t destructor + * @compare: compare function for key + * @hash_function: hash function + * + * Creates a hashlist structure with @max_entries entries of @size + * bytes. User must supply @hash_function and @compare function for + * the hashlist. User can also supply @ctor and @dtor for kmem_cache. + **/ +struct hashlist *hashlist_create(int bucketnum, int max_entries, size_t size, + char *name, + void (*ctor)(void *, kmem_cache_t *, unsigned long), + void (*dtor)(void *, kmem_cache_t *, unsigned long), + int (*compare)(void *data, void *hashkey), + __u32 (*hash_function)(void *hashkey)) +{ + int i; + struct hashlist *hl; + + if (!compare || !hash_function) + goto hlfailed; + + hl = kmalloc(sizeof(struct hashlist), GFP_ATOMIC); + if (!hl) goto hlfailed; + + hl->kmem = kmem_cache_create(name, size, 0, 0, ctor, dtor); + if (!hl->kmem) goto poolfailed; + + hl->hashtable = kmalloc( + sizeof(struct list_head) * bucketnum, GFP_ATOMIC); + if (!hl->hashtable) goto hashfailed; + + for (i = 0; i < bucketnum; i++) + INIT_LIST_HEAD(&hl->hashtable[i]); + + INIT_LIST_HEAD(&hl->sortedlist); + + hl->maxcount = max_entries; + hl->count = 0; + hl->bucketnum = bucketnum; + hl->compare = compare; + hl->hash_function = hash_function; + + return hl; + +hashfailed: + kmem_cache_destroy(hl->kmem); + hl->kmem = NULL; + +poolfailed: + kfree(hl); + +hlfailed: + DEBUG(DBG_ERROR, "could not create hashlist"); + + return NULL; +} + +/** + * hashlist_destroy - Destroy hashlist + * @hashlist: hashlist to destroy + * + * Frees all memory allocated for a hashlist. + **/ +void hashlist_destroy(struct hashlist *hashlist) +{ + DEBUG_FUNC(); + + if (hashlist == NULL) return; + + if (hashlist->hashtable) { + kfree(hashlist->hashtable); + hashlist->hashtable = NULL; + } + + if (hashlist->kmem) { + kmem_cache_destroy(hashlist->kmem); + hashlist->kmem = NULL; + } + + kfree(hashlist); + + return; +} + +/* + * Insert a chain of entries to hashlist into correct order. The + * entries are assumed to have valid hashkeys. We use time_after_eq + * for comparing, since it handles wrap-around correctly, and the + * sortkey is usually jiffies. + */ +static void sorted_insert(struct list_head *lh, struct hashlist_entry *he) +{ + struct list_head *p; + struct hashlist_entry *hlp = NULL; + unsigned long sortkey = he->sortkey; + + if (list_empty(lh)) { + list_add(&he->sorted, lh); + return; + } + + list_for_each(p, lh) { + hlp = list_entry(p, typeof(*hlp), sorted); + if (time_after_eq(hlp->sortkey, sortkey)) { + list_add(&he->sorted, hlp->sorted.prev); + return; + } + } + list_add(&he->sorted, &hlp->sorted); +} + +/** + * hashlist_iterate - Apply function for all elements in a hash list + * @hashlist: pointer to hashlist + * @args: data to pass to the function + * @func: pointer to a function + * + * Apply arbitrary function @func to all elements in a hash list. + * @func must be a pointer to a function with the following prototype: + * int func(void *entry, void *arg, struct in6_addr *hashkey, unsigned + * long *sortkey). Function must return %ITERATOR_STOP, + * %ITERATOR_CONT or %ITERATOR_DELETE_ENTRY. %ITERATOR_STOP stops + * iterator and returns last return value from the function. + * %ITERATOR_CONT continues with iteration. %ITERATOR_DELETE_ENTRY + * deletes current entry from the hashlist. If function changes + * hashlist element's sortkey, iterator automatically schedules + * element to be reinserted after all elements have been processed. + */ +int hashlist_iterate( + struct hashlist *hashlist, void *args, + hashlist_iterator_t func) +{ + int res = ITERATOR_CONT; + unsigned long skey; + struct list_head *p, *n, repos; + struct hashlist_entry *he; + + DEBUG_FUNC(); + INIT_LIST_HEAD(&repos); + + list_for_each_safe(p, n, &hashlist->sortedlist) { + he = list_entry(p, typeof(*he), sorted); + if (res == ITERATOR_STOP) + break; + skey = he->sortkey; + res = func(he, args, &he->sortkey); + if (res == ITERATOR_DELETE_ENTRY) { + hashlist_delete(hashlist, he); + hashlist_free(hashlist, he); + } else if (skey != he->sortkey) { + /* iterator changed the sortkey, schedule for + * repositioning */ + list_move(&he->sorted, &repos); + } + } + list_for_each_safe(p, n, &repos) { + he = list_entry(p, typeof(*he), sorted); + sorted_insert(&hashlist->sortedlist, he); + } + return res; +} + +/** + * hashlist_alloc - Allocate memory for a hashlist entry + * @hashlist: hashlist for allocated entry + * @size: size of entry in bytes + * + * Allocates @size bytes memory from @hashlist->kmem. + **/ +void *hashlist_alloc(struct hashlist *hashlist, int type) +{ + if (hashlist == NULL) return NULL; + return kmem_cache_alloc(hashlist->kmem, type); +} + +/** + * hashlist_free - Free hashlist entry + * @hashlist: hashlist where @he is + * @he: entry to free + * + * Frees an allocated hashlist entry. + **/ +void hashlist_free(struct hashlist *hashlist, struct hashlist_entry *he) +{ + kmem_cache_free(hashlist->kmem, he); +} + +/** + * hashlist_add - Add element to hashlist + * @hashlist: pointer to hashlist + * @hashkey: hashkey for the element + * @sortkey: key for sorting + * @data: element data + * + * Add element to hashlist. Hashlist is also sorted in a linked list + * by @sortkey. + */ +int hashlist_add(struct hashlist *hashlist, void *hashkey, + unsigned long sortkey, void *entry) +{ + struct hashlist_entry *he = (struct hashlist_entry *)entry; + unsigned int hash; + + if (hashlist->count >= hashlist->maxcount) + return -1; + + hashlist->count++; + + /* link the entry to sorted order */ + he->sortkey = sortkey; + sorted_insert(&hashlist->sortedlist, he); + + /* hash the entry */ + hash = hashlist->hash_function(hashkey) % hashlist->bucketnum; + list_add(&he->hashlist, &hashlist->hashtable[hash]); + + return 0; +} + +/** + * hashlist_get_ex - Get element from hashlist + * @hashlist: hashlist + * @hashkey: hashkey of the desired entry + * + * Lookup entry with @hashkey from the hash table using @compare + * function for entry comparison. Returns entry on success, otherwise + * %NULL. + **/ +struct hashlist_entry *hashlist_get_ex( + struct hashlist *hashlist, void *hashkey, + int (*compare)(void *data, void *hashkey)) +{ + struct list_head *p, *bkt; + __u32 hash; + + hash = hashlist->hash_function(hashkey) % hashlist->bucketnum; + bkt = &hashlist->hashtable[hash]; + + /* scan the entries within the same hashbucket */ + list_for_each(p, bkt) { + struct hashlist_entry *he = list_entry(p, typeof(*he), + hashlist); + if (compare(he, hashkey) == 0) + return he; + } + + return NULL; +} + +/** + * hashlist_get - Get element from hashlist + * @hashlist: hashlist + * @hashkey: hashkey of the desired entry + * + * Lookup entry with @hashkey from the hash table. Returns entry on + * success, otherwise %NULL. + **/ +struct hashlist_entry *hashlist_get(struct hashlist *hashlist, void *hashkey) +{ + return hashlist_get_ex(hashlist, hashkey, hashlist->compare); +} + +/** + * hashlist_reposition - set entry to new position in the list + * @hashlist: hashlist + * @he: entry to reposition + * @sortkey: new sortkey of the entry + * + * If secondary order sortkey changes, entry must be repositioned in + * the sorted list. + **/ +int hashlist_reposition(struct hashlist *hashlist, struct hashlist_entry *he, + unsigned long sortkey) +{ + list_del(&he->sorted); + he->sortkey = sortkey; + sorted_insert(&hashlist->sortedlist, he); + + return 0; +} + +/** + * hashlist_delete - Delete entry from hashlist + * @hashlist: hashlist where entry is + * @he: entry to delete + * + * Deletes an entry from the hashlist and sorted list. + **/ +void hashlist_delete(struct hashlist *hashlist, + struct hashlist_entry *he) +{ + list_del_init(&he->hashlist); + list_del_init(&he->sorted); + + hashlist->count--; +} + +/** + * hashlist_get_first - Get first item from sorted list + * @hashlist: pointer to hashlist + * + * Returns first item in the secondary sort order. + **/ +void * hashlist_get_first(struct hashlist *hashlist) +{ + if (list_empty(&hashlist->sortedlist)) + return NULL; + + return list_entry(hashlist->sortedlist.next, struct hashlist_entry, sorted); +} diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/hashlist.h linux-2.4.25/net/ipv6/mobile_ip6/hashlist.h --- linux-2.4.25.old/net/ipv6/mobile_ip6/hashlist.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.25/net/ipv6/mobile_ip6/hashlist.h 2004-06-26 11:29:31.000000000 +0100 @@ -0,0 +1,63 @@ +/* + * MIPL Mobile IPv6 Hashlist header file + * + * $Id$ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _HASHLIST_H +#define _HASHLIST_H + +#define ITERATOR_ERR -1 +#define ITERATOR_CONT 0 +#define ITERATOR_STOP 1 +#define ITERATOR_DELETE_ENTRY 2 + +struct kmem_cache_t; + +struct hashlist_entry { + unsigned long sortkey; + struct list_head sorted; + struct list_head hashlist; +}; + +struct hashlist * hashlist_create( + int bucketnum, int max_entries, size_t size, char *name, + void (*ctor)(void *, kmem_cache_t *, unsigned long), + void (*dtor)(void *, kmem_cache_t *, unsigned long), + int (*compare)(void *data, void *hashkey), + __u32 (*hash_function)(void *hashkey)); + +void hashlist_destroy(struct hashlist *hashlist); + +void *hashlist_alloc(struct hashlist *hashlist, int type); + +void hashlist_free(struct hashlist *hashlist, struct hashlist_entry *he); + +struct hashlist_entry *hashlist_get(struct hashlist *hashlist, void *hashkey); + +struct hashlist_entry *hashlist_get_ex( + struct hashlist *hashlist, void *hashkey, + int (*compare)(void *data, void *hashkey)); + +int hashlist_add(struct hashlist *hashlist, void *hashkey, + unsigned long sortkey, void *data); + +void hashlist_delete(struct hashlist *hashlist, struct hashlist_entry *he); + +/* iterator function */ +typedef int (*hashlist_iterator_t)(void *, void *, unsigned long *); + +int hashlist_iterate(struct hashlist *hashlist, void *args, + hashlist_iterator_t func); + +void * hashlist_get_first(struct hashlist *hashlist); + +int hashlist_reposition(struct hashlist *hashlist, struct hashlist_entry *he, + unsigned long sortkey); + +#endif diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/hmac.c linux-2.4.25/net/ipv6/mobile_ip6/hmac.c --- linux-2.4.25.old/net/ipv6/mobile_ip6/hmac.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.25/net/ipv6/mobile_ip6/hmac.c 2004-06-26 11:29:31.000000000 +0100 @@ -0,0 +1,658 @@ +/* Authentication algorithms + * + * Authors: + * Alexis Olivereau + * + * $Id$ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Changes: + * Henrik Petander : Cleaned up unused parts + * + */ + +#include +#include +#include +#include +#include + +#include "hmac.h" +#define LROLL(x, s) (((x) << (s)) | ((x) >> (32 - (s)))) + +/* MD5 */ +#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z)))) +#define G(x, y, z) ((y) ^ ((z) & ((x) ^ (y)))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define I(x, y, z) ((y) ^ ((x) | ~(z))) + +#define FF(a, b, c, d, m, s, t) { \ + (a) += F ((b), (c), (d)) + (m) + (t); \ + (a) = LROLL((a), (s)); \ + (a) += (b); \ + } +#define GG(a, b, c, d, m, s, t) { \ + (a) += G ((b), (c), (d)) + (m) + (t); \ + (a) = LROLL((a), (s)); \ + (a) += (b); \ + } +#define HH(a, b, c, d, m, s, t) { \ + (a) += H ((b), (c), (d)) + (m) + (t); \ + (a) = LROLL((a), (s)); \ + (a) += (b); \ + } +#define II(a, b, c, d, m, s, t) { \ + (a) += I ((b), (c), (d)) + (m) + (t); \ + (a) = LROLL((a), (s)); \ + (a) += (b); \ + } + +#define s11 7 +#define s12 12 +#define s13 17 +#define s14 22 +#define s21 5 +#define s22 9 +#define s23 14 +#define s24 20 +#define s31 4 +#define s32 11 +#define s33 16 +#define s34 23 +#define s41 6 +#define s42 10 +#define s43 15 +#define s44 21 + +/* SHA-1 */ +#define f(x, y, z) ((z) ^ ((x) & ((y) ^ (z)))) +#define g(x, y, z) (((x) & (y)) | ((x) & (z)) | ((y) & (z))) +#define h(x, y, z) ((x) ^ (y) ^ (z)) + +#define K1 0x5a827999 +#define K2 0x6ed9eba1 +#define K3 0x8f1bbcdc +#define K4 0xca62c1d6 + +int ah_hmac_md5_init(struct ah_processing *ahp, u_int8_t *key, u_int32_t key_len) +{ + int i; + int key_up4; + uint32_t ipad = 0x36363636; + uint8_t extkey[64]; + + ahp->key_auth = key; + ahp->key_auth_len = key_len; + ahp->context = (void *) kmalloc(sizeof(MD5_CTX), GFP_ATOMIC); + if (ahp->context == NULL) + return -1; + md5_init((MD5_CTX *) ahp->context); + if ((64 * sizeof(uint8_t)) < ahp->key_auth_len) { + printk("buffer overflow!"); + return -1; + } + memcpy(extkey, ahp->key_auth, ahp->key_auth_len); + if (ahp->key_auth_len % 4) { + memset(extkey + ahp->key_auth_len, 0, + 4 - (ahp->key_auth_len % 4)); + } + key_up4 = ((ahp->key_auth_len + 0x3) & 0xFFFFFFFC) / 4; + + for (i = 0; i < key_up4; i++) + ((uint32_t *) extkey)[i] = ((uint32_t *) extkey)[i] ^ ipad; + for (i = key_up4; i < 16; i++) + ((uint32_t *) extkey)[i] = ipad; + + md5_compute((MD5_CTX *) ahp->context, extkey, 64); + return 0; +} + +void ah_hmac_md5_loop(struct ah_processing *ahp, void *str, uint32_t len) +{ + md5_compute((MD5_CTX *) ahp->context, str, len); +} + +void ah_hmac_md5_result(struct ah_processing *ahp, char *digest) +{ + uint8_t inner[HMAC_MD5_HASH_LEN]; + int i; + int key_up4; + uint32_t opad = 0x5c5c5c5c; + uint8_t extkey[64]; + + md5_final((MD5_CTX *) ahp->context, inner); + md5_init((MD5_CTX *) ahp->context); + + memcpy(extkey, ahp->key_auth, ahp->key_auth_len); + if (ahp->key_auth_len % 4) { + memset(extkey + ahp->key_auth_len, 0, + 4 - (ahp->key_auth_len % 4)); + } + key_up4 = ((ahp->key_auth_len + 0x3) & 0xFFFFFFFC) / 4; + + for (i = 0; i < key_up4; i++) + ((uint32_t *) extkey)[i] = ((uint32_t *) extkey)[i] ^ opad; + for (i = key_up4; i < 16; i++) + ((uint32_t *) extkey)[i] = opad; + + md5_compute((MD5_CTX *) ahp->context, extkey, 64); + md5_compute((MD5_CTX *) ahp->context, inner, HMAC_MD5_HASH_LEN); + + md5_final((MD5_CTX *) ahp->context, digest); + + kfree(ahp->context); +} + +int ah_hmac_sha1_init(struct ah_processing *ahp, u_int8_t *key, u_int32_t key_len) +{ + int i; + int key_up4; + uint32_t ipad = 0x36363636; + uint8_t extkey[64]; + + ahp->key_auth = key; + ahp->key_auth_len = key_len; + + ahp->context = (void *) kmalloc(sizeof(SHA1_CTX), GFP_ATOMIC); + //if (ahp->context == NULL) + // return -1; + + sha1_init((SHA1_CTX *) ahp->context); + + memcpy(extkey, ahp->key_auth, ahp->key_auth_len); + if (ahp->key_auth_len % 4) { + memset(extkey + ahp->key_auth_len, 0, + 4 - (ahp->key_auth_len % 4)); + } + key_up4 = ((ahp->key_auth_len + 0x3) & 0xFFFFFFFC) / 4; + + for (i = 0; i < key_up4; i++) + ((uint32_t *) extkey)[i] = ((uint32_t *) extkey)[i] ^ ipad; + for (i = key_up4; i < 16; i++) + ((uint32_t *) extkey)[i] = ipad; + + sha1_compute((SHA1_CTX *) ahp->context, extkey, 64); + return 0; +} + +void ah_hmac_sha1_loop(struct ah_processing *ahp, void *str, uint32_t len) +{ + if (!ahp) + return; + sha1_compute((SHA1_CTX *) ahp->context, str, len); +} + +void ah_hmac_sha1_result(struct ah_processing *ahp, char *digest) +{ + uint8_t inner[HMAC_SHA1_HASH_LEN]; + int i; + int key_up4; + uint32_t opad = 0x5c5c5c5c; + uint8_t extkey[64]; + + if (!ahp) + return; + sha1_final((SHA1_CTX *) ahp->context, inner); + sha1_init((SHA1_CTX *) ahp->context); + + memcpy(extkey, ahp->key_auth, ahp->key_auth_len); + if (ahp->key_auth_len % 4) { + memset(extkey + ahp->key_auth_len, 0, + 4 - (ahp->key_auth_len % 4)); + } + key_up4 = ((ahp->key_auth_len + 0x3) & 0xFFFFFFFC) / 4; + + for (i = 0; i < key_up4; i++) + ((uint32_t *) extkey)[i] = ((uint32_t *) extkey)[i] ^ opad; + for (i = key_up4; i < 16; i++) + ((uint32_t *) extkey)[i] = opad; + + sha1_compute((SHA1_CTX *) ahp->context, extkey, 64); + sha1_compute((SHA1_CTX *) ahp->context, inner, + HMAC_SHA1_HASH_LEN); + + sha1_final((SHA1_CTX *) ahp->context, digest); + + kfree(ahp->context); +} + +void md5_init(MD5_CTX * ctx) +{ + ctx->A = 0x67452301; + ctx->B = 0xefcdab89; + ctx->C = 0x98badcfe; + ctx->D = 0x10325476; + ctx->buf_cur = ctx->buf; + ctx->bitlen[0] = ctx->bitlen[1] = 0; + memset(ctx->buf, 0, 64); +} + +void md5_over_block(MD5_CTX * ctx, uint8_t * data) +{ + uint32_t M[16]; + uint32_t a = ctx->A; + uint32_t b = ctx->B; + uint32_t c = ctx->C; + uint32_t d = ctx->D; + + create_M_blocks(M, data); + + /* Round 1 */ + FF(a, b, c, d, M[0], s11, 0xd76aa478); /* 1 */ + FF(d, a, b, c, M[1], s12, 0xe8c7b756); /* 2 */ + FF(c, d, a, b, M[2], s13, 0x242070db); /* 3 */ + FF(b, c, d, a, M[3], s14, 0xc1bdceee); /* 4 */ + FF(a, b, c, d, M[4], s11, 0xf57c0faf); /* 5 */ + FF(d, a, b, c, M[5], s12, 0x4787c62a); /* 6 */ + FF(c, d, a, b, M[6], s13, 0xa8304613); /* 7 */ + FF(b, c, d, a, M[7], s14, 0xfd469501); /* 8 */ + FF(a, b, c, d, M[8], s11, 0x698098d8); /* 9 */ + FF(d, a, b, c, M[9], s12, 0x8b44f7af); /* 10 */ + FF(c, d, a, b, M[10], s13, 0xffff5bb1); /* 11 */ + FF(b, c, d, a, M[11], s14, 0x895cd7be); /* 12 */ + FF(a, b, c, d, M[12], s11, 0x6b901122); /* 13 */ + FF(d, a, b, c, M[13], s12, 0xfd987193); /* 14 */ + FF(c, d, a, b, M[14], s13, 0xa679438e); /* 15 */ + FF(b, c, d, a, M[15], s14, 0x49b40821); /* 16 */ + + /* Round 2 */ + GG(a, b, c, d, M[1], s21, 0xf61e2562); /* 17 */ + GG(d, a, b, c, M[6], s22, 0xc040b340); /* 18 */ + GG(c, d, a, b, M[11], s23, 0x265e5a51); /* 19 */ + GG(b, c, d, a, M[0], s24, 0xe9b6c7aa); /* 20 */ + GG(a, b, c, d, M[5], s21, 0xd62f105d); /* 21 */ + GG(d, a, b, c, M[10], s22, 0x02441453); /* 22 */ + GG(c, d, a, b, M[15], s23, 0xd8a1e681); /* 23 */ + GG(b, c, d, a, M[4], s24, 0xe7d3fbc8); /* 24 */ + GG(a, b, c, d, M[9], s21, 0x21e1cde6); /* 25 */ + GG(d, a, b, c, M[14], s22, 0xc33707d6); /* 26 */ + GG(c, d, a, b, M[3], s23, 0xf4d50d87); /* 27 */ + GG(b, c, d, a, M[8], s24, 0x455a14ed); /* 28 */ + GG(a, b, c, d, M[13], s21, 0xa9e3e905); /* 29 */ + GG(d, a, b, c, M[2], s22, 0xfcefa3f8); /* 30 */ + GG(c, d, a, b, M[7], s23, 0x676f02d9); /* 31 */ + GG(b, c, d, a, M[12], s24, 0x8d2a4c8a); /* 32 */ + + /* Round 3 */ + HH(a, b, c, d, M[5], s31, 0xfffa3942); /* 33 */ + HH(d, a, b, c, M[8], s32, 0x8771f681); /* 34 */ + HH(c, d, a, b, M[11], s33, 0x6d9d6122); /* 35 */ + HH(b, c, d, a, M[14], s34, 0xfde5380c); /* 36 */ + HH(a, b, c, d, M[1], s31, 0xa4beea44); /* 37 */ + HH(d, a, b, c, M[4], s32, 0x4bdecfa9); /* 38 */ + HH(c, d, a, b, M[7], s33, 0xf6bb4b60); /* 39 */ + HH(b, c, d, a, M[10], s34, 0xbebfbc70); /* 40 */ + HH(a, b, c, d, M[13], s31, 0x289b7ec6); /* 41 */ + HH(d, a, b, c, M[0], s32, 0xeaa127fa); /* 42 */ + HH(c, d, a, b, M[3], s33, 0xd4ef3085); /* 43 */ + HH(b, c, d, a, M[6], s34, 0x4881d05); /* 44 */ + HH(a, b, c, d, M[9], s31, 0xd9d4d039); /* 45 */ + HH(d, a, b, c, M[12], s32, 0xe6db99e5); /* 46 */ + HH(c, d, a, b, M[15], s33, 0x1fa27cf8); /* 47 */ + HH(b, c, d, a, M[2], s34, 0xc4ac5665); /* 48 */ + + /* Round 4 */ + II(a, b, c, d, M[0], s41, 0xf4292244); /* 49 */ + II(d, a, b, c, M[7], s42, 0x432aff97); /* 50 */ + II(c, d, a, b, M[14], s43, 0xab9423a7); /* 51 */ + II(b, c, d, a, M[5], s44, 0xfc93a039); /* 52 */ + II(a, b, c, d, M[12], s41, 0x655b59c3); /* 53 */ + II(d, a, b, c, M[3], s42, 0x8f0ccc92); /* 54 */ + II(c, d, a, b, M[10], s43, 0xffeff47d); /* 55 */ + II(b, c, d, a, M[1], s44, 0x85845dd1); /* 56 */ + II(a, b, c, d, M[8], s41, 0x6fa87e4f); /* 57 */ + II(d, a, b, c, M[15], s42, 0xfe2ce6e0); /* 58 */ + II(c, d, a, b, M[6], s43, 0xa3014314); /* 59 */ + II(b, c, d, a, M[13], s44, 0x4e0811a1); /* 60 */ + II(a, b, c, d, M[4], s41, 0xf7537e82); /* 61 */ + II(d, a, b, c, M[11], s42, 0xbd3af235); /* 62 */ + II(c, d, a, b, M[2], s43, 0x2ad7d2bb); /* 63 */ + II(b, c, d, a, M[9], s44, 0xeb86d391); /* 64 */ + + ctx->A += a; + ctx->B += b; + ctx->C += c; + ctx->D += d; +} + +void create_M_blocks(uint32_t * M, uint8_t * data) +{ +#ifdef HAVE_LITTLE_ENDIAN + memcpy((uint8_t *) M, data, 64); +#endif /* HAVE_LITTLE_ENDIAN */ + +#ifdef HAVE_BIG_ENDIAN + int i; + for (i = 0; i < 16; i++, data += 4) { + ((uint8_t *) (&M[i]))[0] = data[3]; + ((uint8_t *) (&M[i]))[1] = data[2]; + ((uint8_t *) (&M[i]))[2] = data[1]; + ((uint8_t *) (&M[i]))[3] = data[0]; + } +#endif /* HAVE_BIG_ENDIAN */ +} + +void md5_compute(MD5_CTX * ctx, uint8_t * data, uint32_t len) +{ + uint8_t pos = ((ctx->bitlen[0] >> 3) & 0x3f); + + /* First we update the bit length */ + if ((ctx->bitlen[0] += (len << 3)) < (len << 3)) + ctx->bitlen[1]++; + ctx->bitlen[1] += (len >> 29); /* len is expressed in bytes */ + + if (pos) { + /* Buffer is not empty */ + if (64 - pos >= len) { + memcpy(ctx->buf_cur, data, len); + ctx->buf_cur += len; + pos += len; + if (pos == 64) { + /* The current block is over */ + md5_over_block(ctx, ctx->buf); + ctx->buf_cur = ctx->buf; + } + return; + } else { + memcpy(ctx->buf_cur, data, 64 - pos); + md5_over_block(ctx, ctx->buf); + len -= (64 - pos); + data += (64 - pos); + ctx->buf_cur = ctx->buf; + } + } + while (len >= 64) { + md5_over_block(ctx, data); + len -= 64; + data += 64; + } + if (len) { + memcpy(ctx->buf_cur, data, len); + ctx->buf_cur += len; + } +} + +void md5_final(MD5_CTX * ctx, uint8_t * digest) +{ + uint32_t rem_size; + uint8_t *buf_cur = ctx->buf_cur; + int i; + + rem_size = 64 - ((ctx->bitlen[0] >> 3) & 0x3f); + *(buf_cur++) = 0x80; + + if (rem_size > 8 + 1) { + /* We have enough room in the current block */ + for (i = 0; i < rem_size - 8 - 1; i++) { + *(buf_cur++) = 0; + } + } else { + /* We do not have enough room and need therefore to add a new + 64-byte block */ + for (i = 0; i < rem_size - 1; i++) { + *(buf_cur++) = 0; + } + md5_over_block(ctx, ctx->buf); + + buf_cur = ctx->buf; + for (i = 0; i < 64 - 8; i++) { + *(buf_cur++) = 0; + } + } +#ifdef HAVE_LITTLE_ENDIAN + memcpy(buf_cur, (uint8_t *) ctx->bitlen, 8); +#endif /* HAVE_LITTLE_ENDIAN */ + +#ifdef HAVE_BIG_ENDIAN + *(buf_cur++) = (ctx->bitlen[0] >> 24) & 0xff; + *(buf_cur++) = (ctx->bitlen[0] >> 16) & 0xff; + *(buf_cur++) = (ctx->bitlen[0] >> 8) & 0xff; + *(buf_cur++) = (ctx->bitlen[0] >> 0) & 0xff; + *(buf_cur++) = (ctx->bitlen[1] >> 24) & 0xff; + *(buf_cur++) = (ctx->bitlen[1] >> 16) & 0xff; + *(buf_cur++) = (ctx->bitlen[1] >> 8) & 0xff; + *(buf_cur++) = (ctx->bitlen[1] >> 0) & 0xff; +#endif /* HAVE_BIG_ENDIAN */ + + md5_over_block(ctx, ctx->buf); + +#ifdef HAVE_LITTLE_ENDIAN + memcpy(digest + 0, (uint8_t *) (&(ctx->A)), sizeof(uint32_t)); + memcpy(digest + 4, (uint8_t *) (&(ctx->B)), sizeof(uint32_t)); + memcpy(digest + 8, (uint8_t *) (&(ctx->C)), sizeof(uint32_t)); + memcpy(digest + 12, (uint8_t *) (&(ctx->D)), sizeof(uint32_t)); +#endif /* HAVE_LITTLE_ENDIAN */ + +#ifdef HAVE_BIG_ENDIAN + digest[0] = ((ctx->A) >> 24) & 0xff; + digest[1] = ((ctx->A) >> 16) & 0xff; + digest[2] = ((ctx->A) >> 8) & 0xff; + digest[3] = ((ctx->A) >> 0) & 0xff; + digest[4] = ((ctx->B) >> 24) & 0xff; + digest[5] = ((ctx->B) >> 16) & 0xff; + digest[6] = ((ctx->B) >> 8) & 0xff; + digest[7] = ((ctx->B) >> 0) & 0xff; + digest[8] = ((ctx->C) >> 24) & 0xff; + digest[9] = ((ctx->C) >> 16) & 0xff; + digest[10] = ((ctx->C) >> 8) & 0xff; + digest[11] = ((ctx->C) >> 0) & 0xff; + digest[12] = ((ctx->D) >> 24) & 0xff; + digest[13] = ((ctx->D) >> 16) & 0xff; + digest[14] = ((ctx->D) >> 8) & 0xff; + digest[15] = ((ctx->D) >> 0) & 0xff; +#endif /* HAVE_BIG_ENDIAN */ +} + +void sha1_init(SHA1_CTX * ctx) +{ + ctx->A = 0x67452301; + ctx->B = 0xefcdab89; + ctx->C = 0x98badcfe; + ctx->D = 0x10325476; + ctx->E = 0xc3d2e1f0; + ctx->buf_cur = ctx->buf; + ctx->bitlen[0] = ctx->bitlen[1] = 0; + memset(ctx->buf, 0, 64); +} + +void sha1_over_block(SHA1_CTX * ctx, uint8_t * data) +{ + int i; + uint32_t W[80]; + uint32_t a = ctx->A; + uint32_t b = ctx->B; + uint32_t c = ctx->C; + uint32_t d = ctx->D; + uint32_t e = ctx->E; + uint32_t temp; + + create_W_blocks(W, data); + + /* Round 1 */ + for (i = 0; i < 20; i++) { + temp = LROLL(a, 5) + f(b, c, d) + e + W[i] + K1; + e = d; + d = c; + c = LROLL(b, 30); + b = a; + a = temp; + } + + /* Round 2 */ + for (i = 20; i < 40; i++) { + temp = LROLL(a, 5) + h(b, c, d) + e + W[i] + K2; + e = d; + d = c; + c = LROLL(b, 30); + b = a; + a = temp; + } + + /* Round 3 */ + for (i = 40; i < 60; i++) { + temp = LROLL(a, 5) + g(b, c, d) + e + W[i] + K3; + e = d; + d = c; + c = LROLL(b, 30); + b = a; + a = temp; + } + + /* Round 4 */ + for (i = 60; i < 80; i++) { + temp = LROLL(a, 5) + h(b, c, d) + e + W[i] + K4; + e = d; + d = c; + c = LROLL(b, 30); + b = a; + a = temp; + } + + ctx->A += a; + ctx->B += b; + ctx->C += c; + ctx->D += d; + ctx->E += e; +} + +void create_W_blocks(uint32_t * W, uint8_t * data) +{ + int i; + +#ifdef HAVE_BIG_ENDIAN + memcpy((uint8_t *) W, data, 64); +#endif /* HAVE_BIG_ENDIAN */ + +#ifdef HAVE_LITTLE_ENDIAN + for (i = 0; i < 16; i++, data += 4) { + ((uint8_t *) (&W[i]))[0] = data[3]; + ((uint8_t *) (&W[i]))[1] = data[2]; + ((uint8_t *) (&W[i]))[2] = data[1]; + ((uint8_t *) (&W[i]))[3] = data[0]; + } +#endif /* HAVE_LITTLE_ENDIAN */ + for (i = 16; i < 80; i++) { + W[i] = W[i - 3] ^ W[i - 8] ^ W[i - 14] ^ W[i - 16]; + W[i] = LROLL(W[i], 1); + } +} + +void sha1_compute(SHA1_CTX * ctx, uint8_t * data, uint32_t len) +{ + uint8_t pos = ((ctx->bitlen[0] >> 3) & 0x3f); + + /* First we update the bit length */ + if ((ctx->bitlen[0] += (len << 3)) < (len << 3)) + ctx->bitlen[1]++; + ctx->bitlen[1] += (len >> 29); /* len is expressed in bytes */ + + if (pos) { + /* Buffer is not empty */ + if (64 - pos >= len) { + memcpy(ctx->buf_cur, data, len); + ctx->buf_cur += len; + pos += len; + if (pos == 64) { + /* The current block is over */ + sha1_over_block(ctx, ctx->buf); + ctx->buf_cur = ctx->buf; + } + return; + } else { + memcpy(ctx->buf_cur, data, 64 - pos); + sha1_over_block(ctx, ctx->buf); + len -= (64 - pos); + data += (64 - pos); + ctx->buf_cur = ctx->buf; + } + } + while (len >= 64) { + sha1_over_block(ctx, data); + len -= 64; + data += 64; + } + if (len) { + memcpy(ctx->buf_cur, data, len); + ctx->buf_cur += len; + } +} + +void sha1_final(SHA1_CTX * ctx, uint8_t * digest) +{ + uint32_t rem_size; + uint8_t *buf_cur = ctx->buf_cur; + int i; + + rem_size = 64 - ((ctx->bitlen[0] >> 3) & 0x3f); + *(buf_cur++) = 0x80; + + if (rem_size > 8 + 1) { + /* We have enough room in the current block */ + for (i = 0; i < rem_size - 8 - 1; i++) { + *(buf_cur++) = 0; + } + } else { + /* We do not have enough room and need therefore to add a new + 64-byte block */ + for (i = 0; i < rem_size - 1; i++) { + *(buf_cur++) = 0; + } + sha1_over_block(ctx, ctx->buf); + + buf_cur = ctx->buf; + for (i = 0; i < 64 - 8; i++) { + *(buf_cur++) = 0; + } + } +#ifdef HAVE_BIG_ENDIAN + memcpy(buf_cur, (uint8_t *) ctx->bitlen, 8); +#endif /* HAVE_BIG_ENDIAN */ + +#ifdef HAVE_LITTLE_ENDIAN + *(buf_cur++) = (ctx->bitlen[1] >> 24) & 0xff; + *(buf_cur++) = (ctx->bitlen[1] >> 16) & 0xff; + *(buf_cur++) = (ctx->bitlen[1] >> 8) & 0xff; + *(buf_cur++) = (ctx->bitlen[1] >> 0) & 0xff; + *(buf_cur++) = (ctx->bitlen[0] >> 24) & 0xff; + *(buf_cur++) = (ctx->bitlen[0] >> 16) & 0xff; + *(buf_cur++) = (ctx->bitlen[0] >> 8) & 0xff; + *(buf_cur++) = (ctx->bitlen[0] >> 0) & 0xff; +#endif /* HAVE_LITTLE_ENDIAN */ + + sha1_over_block(ctx, ctx->buf); + +#ifdef HAVE_BIG_ENDIAN + memcpy(digest + 0, (uint8_t *) (&(ctx->A)), sizeof(uint32_t)); + memcpy(digest + 4, (uint8_t *) (&(ctx->B)), sizeof(uint32_t)); + memcpy(digest + 8, (uint8_t *) (&(ctx->C)), sizeof(uint32_t)); + memcpy(digest + 12, (uint8_t *) (&(ctx->D)), sizeof(uint32_t)); + memcpy(digest + 16, (uint8_t *) (&(ctx->E)), sizeof(uint32_t)); +#endif /* HAVE_BIG_ENDIAN */ + +#ifdef HAVE_LITTLE_ENDIAN + digest[0] = ((ctx->A) >> 24) & 0xff; + digest[1] = ((ctx->A) >> 16) & 0xff; + digest[2] = ((ctx->A) >> 8) & 0xff; + digest[3] = ((ctx->A) >> 0) & 0xff; + digest[4] = ((ctx->B) >> 24) & 0xff; + digest[5] = ((ctx->B) >> 16) & 0xff; + digest[6] = ((ctx->B) >> 8) & 0xff; + digest[7] = ((ctx->B) >> 0) & 0xff; + digest[8] = ((ctx->C) >> 24) & 0xff; + digest[9] = ((ctx->C) >> 16) & 0xff; + digest[10] = ((ctx->C) >> 8) & 0xff; + digest[11] = ((ctx->C) >> 0) & 0xff; + digest[12] = ((ctx->D) >> 24) & 0xff; + digest[13] = ((ctx->D) >> 16) & 0xff; + digest[14] = ((ctx->D) >> 8) & 0xff; + digest[15] = ((ctx->D) >> 0) & 0xff; + digest[16] = ((ctx->E) >> 24) & 0xff; + digest[17] = ((ctx->E) >> 16) & 0xff; + digest[18] = ((ctx->E) >> 8) & 0xff; + digest[19] = ((ctx->E) >> 0) & 0xff; +#endif /* HAVE_LITTLE_ENDIAN */ +} diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/hmac.h linux-2.4.25/net/ipv6/mobile_ip6/hmac.h --- linux-2.4.25.old/net/ipv6/mobile_ip6/hmac.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.25/net/ipv6/mobile_ip6/hmac.h 2004-06-26 11:29:31.000000000 +0100 @@ -0,0 +1,94 @@ +/* + * MIPL Mobile IPv6 Message authentication algorithms + * + * $Id$ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _HMAC_H +#define _HMAC_H + +#include +#include + +#define HAVE_LITTLE_ENDIAN + +#define NO_EXPIRY 1 /* For sec_as */ + +#define ALG_AUTH_NONE 0 +#define ALG_AUTH_HMAC_MD5 1 +#define ALG_AUTH_HMAC_SHA1 2 + +struct sec_as; +struct ah_processing { + void *context; + struct sec_as *sas; + u_int8_t *key_auth; + u_int32_t key_auth_len; +}; + +struct antireplay { + u_int32_t count; + u_int32_t bitmap; +}; + +typedef struct { + u_int32_t A, B, C, D; + u_int32_t bitlen[2]; + u_int8_t* buf_cur; + u_int8_t buf[64]; +} MD5_CTX; + +typedef struct { + u_int32_t A, B, C, D, E; + u_int32_t bitlen[2]; + u_int8_t* buf_cur; + u_int8_t buf[64]; +} SHA1_CTX; + + + +int ah_hmac_md5_init (struct ah_processing *ahp, u_int8_t *key, u_int32_t key_len); +void ah_hmac_md5_loop(struct ah_processing*, void*, u_int32_t); +void ah_hmac_md5_result(struct ah_processing*, char*); +int ah_hmac_sha1_init(struct ah_processing*, u_int8_t *key, u_int32_t key_len); +void ah_hmac_sha1_loop(struct ah_processing*, void*, u_int32_t); +void ah_hmac_sha1_result(struct ah_processing*, char*); + + +#define AH_HDR_LEN 12 /* # of bytes for Next Header, Payload Length, + RESERVED, Security Parameters Index and + + Sequence Number Field */ + +void md5_init(MD5_CTX *ctx); +void md5_over_block(MD5_CTX *ctx, u_int8_t* data); +void create_M_blocks(u_int32_t* M, u_int8_t* data); +void md5_compute(MD5_CTX *ctx, u_int8_t* data, u_int32_t len); +void md5_final(MD5_CTX *ctx, u_int8_t* digest); + +void sha1_init(SHA1_CTX *ctx); +void sha1_over_block(SHA1_CTX *ctx, u_int8_t* data); +void create_W_blocks(u_int32_t* W, u_int8_t* data); +void sha1_compute(SHA1_CTX *ctx, u_int8_t* data, u_int32_t len); +void sha1_final(SHA1_CTX *ctx, u_int8_t* digest); + +struct mipv6_acq { + struct in6_addr coa; + struct in6_addr haddr; + struct in6_addr peer; + u_int32_t spi; +}; +#define MIPV6_MAX_AUTH_DATA 20 + +#define HMAC_MD5_HASH_LEN 16 +#define HMAC_SHA1_HASH_LEN 20 +#define HMAC_SHA1_KEY_SIZE 20 +#define HMAC_MD5_ICV_LEN 12 /* RFC 2403 */ +#define HMAC_SHA1_ICV_LEN 12 /* RFC 2404 */ + +#endif /* _HMAC_H */ diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/ioctl_mn.c linux-2.4.25/net/ipv6/mobile_ip6/ioctl_mn.c --- linux-2.4.25.old/net/ipv6/mobile_ip6/ioctl_mn.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.25/net/ipv6/mobile_ip6/ioctl_mn.c 2004-06-26 11:29:31.000000000 +0100 @@ -0,0 +1,142 @@ +/* + * Mobile Node IOCTL Control device + * + * Authors: + * Henrik Petander + * + * $Id$ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "debug.h" +#include "mdetect.h" +#include "multiaccess_ctl.h" + +/* Reserved for local / experimental use */ +#define MAJOR_NUM 0xf9 + +/* Get Care-of address information for Mobile Node */ +#define IOCTL_GET_CAREOFADDR _IOWR(MAJOR_NUM, 9, void *) + +#define MA_IOCTL_SET_IFACE_PREFERENCE _IOR (MAJOR_NUM, 13, void *) + +/* The name of the device file */ +#define CTLFILE "mipv6_dev" + +static int inuse = 0; + +static int mipv6_open(struct inode *inode, struct file *file) +{ + DEBUG(DBG_INFO, "(%p)\n", file); + + if (inuse) + return -EBUSY; + + inuse++; + + MOD_INC_USE_COUNT; + + return 0; +} + +static int mipv6_close(struct inode *inode, struct file *file) +{ + DEBUG(DBG_INFO, "(%p,%p)\n", inode, file); + inuse--; + + MOD_DEC_USE_COUNT; + + return 0; +} + +int mipv6_ioctl(struct inode *inode, struct file *file, + unsigned int ioctl_num, /* The number of the ioctl */ + unsigned long arg) /* The parameter to it */ +{ + struct in6_addr careofaddr; + + /* Switch according to the ioctl called */ + switch (ioctl_num) { + case IOCTL_GET_CAREOFADDR: + DEBUG(DBG_DATADUMP, "IOCTL_GET_CAREOFADDR"); + /* First get home address from user and then look up + * the care-of address and return it + */ + if (copy_from_user(&careofaddr, (struct in6_addr *)arg, + sizeof(struct in6_addr)) < 0) { + DEBUG(DBG_WARNING, "Copy from user failed"); + return -EFAULT; + } + mipv6_get_care_of_address(&careofaddr, &careofaddr); + if (copy_to_user((struct in6_addr *)arg, &careofaddr, + sizeof(struct in6_addr)) < 0) { + DEBUG(DBG_WARNING, "copy_to_user failed"); + return -EFAULT; + } + break; + case MA_IOCTL_SET_IFACE_PREFERENCE: + DEBUG(DBG_INFO, "MA_IOCTL_SET_IFACE_PREFERENCE"); + ma_ctl_set_preference(arg); + break; + + default: + DEBUG(DBG_WARNING, "Unknown ioctl cmd (%d)", ioctl_num); + return -ENOENT; + } + return 0; +} + +struct file_operations Fops = { + owner: THIS_MODULE, + read: NULL, + write: NULL, + poll: NULL, + ioctl: mipv6_ioctl, + open: mipv6_open, + release: mipv6_close +}; + + +/* Initialize the module - Register the character device */ +int mipv6_ioctl_mn_init(void) +{ + int ret_val; + + /* Register the character device (atleast try) */ + ret_val = register_chrdev(MAJOR_NUM, CTLFILE, &Fops); + + /* Negative values signify an error */ + if (ret_val < 0) { + DEBUG(DBG_ERROR, "failed registering char device (err=%d)", + ret_val); + return ret_val; + } + + DEBUG(DBG_INFO, "Device number %x, success", MAJOR_NUM); + return 0; +} + + +/* Cleanup - unregister the appropriate file from /proc */ +void mipv6_ioctl_mn_exit(void) +{ + int ret; + /* Unregister the device */ + ret = unregister_chrdev(MAJOR_NUM, CTLFILE); + + /* If there's an error, report it */ + if (ret < 0) + DEBUG(DBG_ERROR, "errorcode: %d\n", ret); +} diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/mdetect.c linux-2.4.25/net/ipv6/mobile_ip6/mdetect.c --- linux-2.4.25.old/net/ipv6/mobile_ip6/mdetect.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.25/net/ipv6/mobile_ip6/mdetect.c 2004-06-26 11:29:31.000000000 +0100 @@ -0,0 +1,1153 @@ +/* + * Movement Detection Module + * + * Authors: + * Henrik Petander + * + * $Id$ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Handles the L3 movement detection of mobile node and also + * changing of its routes. + * + */ + +/* + * Changes: + * + * Nanno Langstraat : Locking fixes + * Venkata Jagana : Locking fix + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_SYSCTL +#include +#endif /* CONFIG_SYSCTL */ + +#include "util.h" +#include "mdetect.h" +#include "mn.h" +#include "debug.h" +#include "multiaccess_ctl.h" + +#define START 0 +#define CONTINUE 1 +#define OK 2 +#define DEBUG_MDETECT 7 + +#define DEF_RTR_POLL_IVAL 5 /* In seconds */ + +#define NO_RTR 0 +#define RTR_SUSPECT 1 +#define CURR_RTR_OK 2 + +#define RA_RCVD 0 +#define NA_RCVD 1 +#define TIMEOUT 2 + +#define MIPV6_MDF_NONE 0x0 +#define MIPV6_MDF_HAS_RTR_PREV 0x1 + +#define ROUTER_REACHABLE 1 +#define RADV_MISSED 2 +#define NOT_REACHABLE 3 + +/* R_TIME_OUT paramater is used to make the decision when to change the + * default router, if the current one is unreachable. 2s is pretty aggressive + * and may result in hopping between two routers. OTOH a small value enhances + * the performance + */ +#define R_TIME_OUT 30*HZ + +/* maximum RA interval for router unreachability detection */ +#define MAX_RADV_INTERVAL 6*HZ /* 6000 ms... */ + +/* Threshold for exponential resending of router solicitations */ +#define RS_RESEND_LINEAR 10*HZ + +#define EAGER_CELL_SWITCHING 1 +#define LAZY_CELL_SWITCHING 0 +#define RESPECT_DAD 1 + +#define ROUTER_ADDRESS 0x20 + +/* RA flags */ +#define ND_RA_FLAG_MANAGED 0x80 +#define ND_RA_FLAG_OTHER 0x40 +#define ND_RA_FLAG_HA 0x20 + +/* DAD flags for global and link local addresses */ + +#define COA_TENTATIVE 0x10 +#define LLADDR_TENTATIVE 0x01 + +struct router { + struct list_head list; + struct in6_addr ll_addr; + struct in6_addr raddr; /* Also contains prefix */ + __u8 link_addr[MAX_ADDR_LEN]; /* link layer address */ + __u8 link_addr_len; + __u8 state; + __u8 is_current; + __u8 reachable; + int ifindex; + int pfix_len; /* Length of the network prefix */ + unsigned long lifetime; /* from ra */ + __u32 last_ns_sent; + __u32 last_ra_rcvd; + __u32 interval; /* ra interval in milliseconds, 0 if not set */ + int glob_addr; /*Whether raddr contains also routers global address*/ + __u8 flags; /* RA flags, for example ha */ + struct in6_addr CoA; /* care-off address used with this router */ + int extra_addr_route; +}; + +/* dad could also be RESPECT_DAD for duplicate address detection of + new care-of addresses */ +static int dad = 0; + +/* Only one choice, nothing else implemented */ +int max_rtr_reach_time = DEF_RTR_POLL_IVAL; + + +int eager_cell_switching = EAGER_CELL_SWITCHING; /* Can be set to 0 via proc */ +static spinlock_t router_lock; +static spinlock_t ho_lock; + +static void coa_timer_handler(unsigned long arg); +static void timer_handler(unsigned long foo); +static struct router *curr_router = NULL, *next_router = NULL; +static struct timer_list r_timer = { function: timer_handler }; +static struct timer_list coa_timer = { function: coa_timer_handler }; +#define MAX_ROUTERS 1000 +static LIST_HEAD(rtr_list); +static int num_routers = 0; +static struct handoff *_ho = NULL; +/* + * Functions for handling the default router list, which movement + * detection uses for avoiding loops etc. + */ + +/* TODO: Send NS to router after MAX interval has passed from last RA */ +static int mipv6_router_state(struct router *rtr) { + if (rtr->interval) { + if (time_before(jiffies, (rtr->last_ra_rcvd + (rtr->interval * HZ) / 1000))) + return ROUTER_REACHABLE; + else + return NOT_REACHABLE; + } + else + if (time_after(jiffies, rtr->last_ra_rcvd + (rtr->lifetime * HZ))) + return NOT_REACHABLE; + return ROUTER_REACHABLE; +} + +/* searches for a specific router or any router that is reachable, + * if address is NULL. Also deletes obsolete routers. + */ +static void mipv6_router_gc(void) +{ + struct router *curr = NULL; + struct list_head *lh, *lh_tmp; + + DEBUG_FUNC(); + + list_for_each_safe(lh, lh_tmp, &rtr_list) { + curr = list_entry(lh, struct router, list); + if (mipv6_router_state(curr) == NOT_REACHABLE && !curr->is_current) { + num_routers--; + list_del_init(&curr->list); + DEBUG(DBG_DATADUMP, "Deleting unreachable router %x:%x:%x:%x:%x:%x:%x:%x", + NIPV6ADDR(&curr->raddr)); + kfree(curr); + } + else { + DEBUG(DBG_DATADUMP, "NOT Deleting router %x:%x:%x:%x:%x:%x:%x:%x", + NIPV6ADDR(&curr->raddr)); + } + } +} + +static struct router *mipv6_rtr_get(struct in6_addr *search_addr) +{ + struct router *rtr = NULL; + struct list_head *lh; + + DEBUG_FUNC(); + + if (search_addr == NULL) + return NULL; + list_for_each(lh, &rtr_list) { + rtr = list_entry(lh, struct router, list); + if(!ipv6_addr_cmp(search_addr, &rtr->raddr)) { + return rtr; + } + } + return NULL; +} + +/* + * Adds router to list + */ +static struct router *mipv6_rtr_add(struct router *nrt) +{ + + struct router *rptr; + + DEBUG_FUNC(); + + /* check if someone is trying DoS attack, or we just have some + memory leaks... */ + if (num_routers > MAX_ROUTERS) { + DEBUG(DBG_CRITICAL, + "failed to add new router, MAX_ROUTERS exceeded"); + return NULL; + } + + rptr = kmalloc(sizeof(struct router), GFP_ATOMIC); + if (rptr) { + memcpy(rptr, nrt, sizeof(struct router)); + list_add(&rptr->list, &rtr_list); + num_routers++; + } + DEBUG(DBG_INFO, "Adding router: %x:%x:%x:%x:%x:%x:%x:%x, " + "lifetime : %d sec, adv.interval: %d millisec", + NIPV6ADDR(&rptr->raddr), rptr->lifetime, rptr->interval); + + DEBUG(DBG_INFO, "num_routers after addition: %d", num_routers); + return rptr; +} + +/* Cleans up the list */ +static void list_free(struct router **curr_router_p) +{ + struct router *tmp; + struct list_head *lh, *lh_tmp; + + DEBUG_FUNC(); + + DEBUG(DBG_INFO, "Freeing the router list"); + /* set curr_router->prev_router and curr_router NULL */ + *curr_router_p = NULL; + list_for_each_safe(lh, lh_tmp, &rtr_list) { + tmp = list_entry(lh, struct router, list); + DEBUG(DBG_INFO, "%x:%x:%x:%x:%x:%x:%x:%x", + NIPV6ADDR(&tmp->ll_addr)); + list_del(&tmp->list); + kfree(tmp); + num_routers--; + } +} + +int rs_state = START; + +/* Sends router solicitations to all valid devices + * source = link local address (of sending interface) + * dstaddr = all routers multicast address + * Solicitations are sent at an exponentially decreasing rate + * + * TODO: send solicitation first at a normal rate (from ipv6) and + * after that use the exponentially increasing intervals + */ +static int rs_send(void) +{ + struct net_device *dev; + struct in6_addr raddr, lladdr; + struct inet6_dev *in6_dev = NULL; + static int num_rs; + + if (rs_state == START) { + num_rs = 0; + rs_state = CONTINUE; + } else if (num_rs++ > MAX_RTR_SOLICITATIONS) + return HZ; + + ipv6_addr_all_routers(&raddr); + read_lock(&dev_base_lock); + + /* Send router solicitations to all interfaces */ + for (dev = dev_base; dev; dev = dev->next) { + if ((dev->flags & IFF_UP) && dev->type == ARPHRD_ETHER) { + DEBUG(DBG_DATADUMP, "Sending RS to device %s", + dev->name); + if (!ipv6_get_lladdr(dev, &lladdr)) { + ndisc_send_rs(dev, &lladdr, &raddr); + in6_dev = in6_dev_get(dev); + in6_dev->if_flags |= IF_RS_SENT; + in6_dev_put(in6_dev); + } else { + DEBUG(DBG_DATADUMP, "%s: device doesn't have link-local address!\n", dev->name); + continue; + } + } + + } + read_unlock(&dev_base_lock); + return RTR_SOLICITATION_INTERVAL; +} + +/* Create a new CoA for MN and also add a route to it if it is still tentative + to allow MN to get packets to the address immediately + */ +static int form_coa(struct in6_addr *coa, struct in6_addr *pfix, + int plen, int ifindex) +{ + struct net_device *dev; + struct inet6_dev *in6_dev; + int ret = 0; + + if ((dev = dev_get_by_index(ifindex)) == NULL) { + DEBUG(DBG_WARNING, "Device is not present"); + return -1; + } + if ((in6_dev = in6_dev_get(dev)) == NULL) { + DEBUG(DBG_WARNING, "inet6_dev is not present"); + dev_put(dev); + return -1; + } + coa->s6_addr32[0] = pfix->s6_addr32[0]; + coa->s6_addr32[1] = pfix->s6_addr32[1]; + + if (ipv6_generate_eui64(coa->s6_addr + 8, dev) && + ipv6_inherit_eui64(coa->s6_addr + 8, in6_dev)) { + in6_dev_put(in6_dev); + dev_put(dev); + return -1; + } + if (ipv6_chk_addr(coa, dev) == 0) { + DEBUG(DBG_WARNING, "care-of address still tentative"); + ret = 1; + } + DEBUG(DBG_INFO, "Formed new CoA: %x:%x:%x:%x:%x:%x:%x:%x", + NIPV6ADDR(coa)); + + in6_dev_put(in6_dev); + dev_put(dev); + return ret; +} + +static inline int rtr_is_gw(struct router *rtr, struct rt6_info *rt) +{ + return ((rt->rt6i_flags & RTF_GATEWAY) && + !ipv6_addr_cmp(&rt->rt6i_gateway, &rtr->ll_addr)); +} + +static inline int is_prefix_route(struct router *rtr, struct rt6_info *rt) +{ + return (!(rt->rt6i_flags & RTF_GATEWAY) && + mipv6_prefix_compare(&rt->rt6i_dst.addr, &rtr->raddr, + rtr->pfix_len)); +} + +/* + * Function that determines whether given rt6_info should be destroyed + * (negative => destroy rt6_info, zero or positive => do nothing) + */ +static int mn_route_cleaner(struct rt6_info *rt, void *arg) +{ + int type; + + struct router *rtr = (struct router *)arg; + + int ret = -1; + + DEBUG_FUNC(); + + if (!rt || !rtr) { + DEBUG(DBG_ERROR, "mn_route_cleaner: rt or rtr NULL"); + return 0; + } + + /* Do not delete routes to local addresses or to multicast + * addresses, since we need them to get router advertisements + * etc. Multicast addresses are more tricky, but we don't + * delete them in any case. The routing mechanism is not optimal for + * multihoming. + * + * Also keep all new prefix routes, gateway routes through rtr and + * all remaining default routes (including those used for reverse + * tunneling) + */ + type = ipv6_addr_type(&rt->rt6i_dst.addr); + + if ((type & (IPV6_ADDR_MULTICAST | IPV6_ADDR_LINKLOCAL)) || + rt->rt6i_dev == &loopback_dev || rtr_is_gw(rtr, rt) || + is_prefix_route(rtr, rt) || (rt->rt6i_flags & RTF_DEFAULT)) + ret = 0; + + /* delete all others */ + + if (rt->rt6i_dev != &loopback_dev) { + DEBUG(DEBUG_MDETECT, + "%s route:\n" + "dev: %s,\n" + "gw: %x:%x:%x:%x:%x:%x:%x:%x,\n" + "flags: %x,\n" + "metric: %d,\n" + "src: %x:%x:%x:%x:%x:%x:%x:%x,\n" + "dst: %x:%x:%x:%x:%x:%x:%x:%x,\n" + "plen: %d\n", + (ret ? "Deleting" : "Keeping"), + rt->rt6i_dev->name, + NIPV6ADDR(&rt->rt6i_gateway), + rt->rt6i_flags, + rt->rt6i_metric, + NIPV6ADDR(&rt->rt6i_src.addr), + NIPV6ADDR(&rt->rt6i_dst.addr), + rt->rt6i_dst.plen); + } + return ret; +} + +/* + * Deletes old routes + */ +static __inline__ void delete_routes(struct router *rtr) +{ + DEBUG_FUNC(); + + /* Routing table is locked to ensure that nobody uses its */ + write_lock_bh(&rt6_lock); + DEBUG(DBG_INFO, "mipv6: Purging routes"); + /* TODO: Does not prune, should it? */ + fib6_clean_tree(&ip6_routing_table, + mn_route_cleaner, 0, rtr); + write_unlock_bh(&rt6_lock); + +} + + +static __inline__ void delete_coas(struct router *rtr) +{ + struct net_device *dev; + struct inet6_dev *idev; + struct inet6_ifaddr *ifa; + + dev = dev_get_by_index(rtr->ifindex); + if (!dev) + return; + + idev = in6_dev_get(dev); + + if (idev) { + read_lock_bh(&idev->lock); + ifa = idev->addr_list; + while (ifa) { + int keep; + spin_lock(&ifa->lock); + + keep = (ifa->flags&(IFA_F_PERMANENT|IFA_F_HOMEADDR) || + !ipv6_addr_cmp(&ifa->addr, &rtr->CoA)); + + spin_unlock(&ifa->lock); + + if (keep) + ifa = ifa->if_next; + else { + in6_ifa_hold(ifa); + read_unlock_bh(&idev->lock); + + ipv6_del_addr(ifa); + + read_lock_bh(&idev->lock); + ifa = idev->addr_list; + } + } + read_unlock_bh(&idev->lock); + in6_dev_put(idev); + } + dev_put(dev); +} + +int next_mdet_state[3][3] = {{CURR_RTR_OK, NO_RTR, NO_RTR}, + {CURR_RTR_OK, CURR_RTR_OK, NO_RTR}, + {CURR_RTR_OK, CURR_RTR_OK, RTR_SUSPECT}}; + +char *states[3] = {"NO_RTR", "RTR_SUSPECT", "CURR_RTR_OK"}; +char *events[3] = {"RA_RCVD", "NA_RCVD", "TIMEOUT"}; + +/* State transitions + * NO_RTR, RA_RCVD -> CURR_RTR_OK + * NO_RTR, NA_RCVD -> NO_RTR + * NO_RTR, TIMEOUT -> NO_RTR + + * RTR_SUSPECT, RA_RCVD -> CURR_RTR_OK + * RTR_SUSPECT, NA_RCVD -> CURR_RTR_OK + * RTR_SUSPECT, TIMEOUT -> NO_RTR + + * CURR_RTR_OK, RA_RCVD -> CURR_RTR_OK + * CURR_RTR_OK, NA_RCVD -> CURR_RTR_OK + * CURR_RTR_OK, TIMEOUT -> RTR_SUSPECT + */ +static int _curr_state = NO_RTR; + +#if 0 +static int get_mdet_state(void){ + int state; + spin_lock_bh(&router_lock); + state = _curr_state; + spin_unlock_bh(&router_lock); + return state; +} +#endif + +/* Needs to be called with router_lock locked */ +static int mdet_statemachine(int event) +{ + + if (event > 2 || _curr_state > 2) { + DEBUG(DBG_ERROR, "Got illegal event or curr_state"); + return -1; + } + + DEBUG(DBG_DATADUMP, "Got event %s and curr_state is %s", + events[event], states[_curr_state]); + + _curr_state = next_mdet_state[_curr_state][event]; + DEBUG(DBG_DATADUMP, "Next state is %s", states[_curr_state]); + return _curr_state; +} + +static void mipv6_do_ll_dad(int ifindex) +{ + struct net_device *dev = dev_get_by_index(ifindex); + if (dev) { + struct in6_addr lladdr; + struct inet6_ifaddr *ifa; + if (!ipv6_get_lladdr(dev, &lladdr) && + (ifa = ipv6_get_ifaddr(&lladdr, dev)) != NULL) { + spin_lock_bh(&ifa->lock); + if (!(ifa->flags & IFA_F_TENTATIVE)) { + ifa->flags |= IFA_F_TENTATIVE; + spin_unlock_bh(&ifa->lock); + addrconf_dad_start(ifa, 0); + } else + spin_unlock_bh(&ifa->lock); + + } + dev_put(dev); + } +} +/* + * Changes the router, called from ndisc.c if mipv6_router_event + * returns true. + */ + +static void mipv6_change_router(void) +{ + struct in6_addr coa; + int ret, ifindex; + + DEBUG_FUNC(); + + + if (next_router == NULL) + return; + + spin_lock(&router_lock); + + + if (curr_router != NULL && + !ipv6_addr_cmp(&curr_router->ll_addr, &next_router->ll_addr)) { + DEBUG(DBG_INFO,"Trying to handoff from: " + "%x:%x:%x:%x:%x:%x:%x:%x", + NIPV6ADDR(&curr_router->ll_addr)); + DEBUG(DBG_INFO,"Trying to handoff to: " + "%x:%x:%x:%x:%x:%x:%x:%x", + NIPV6ADDR(&next_router->ll_addr)); + next_router = NULL; /* Let's not leave dangling pointers */ + spin_unlock(&router_lock); + return; + } + ret = form_coa(&next_router->CoA, &next_router->raddr, + next_router->pfix_len, next_router->ifindex); + if (ret < 0) { + DEBUG(DBG_ERROR, "handoff: Creation of coa failed"); + spin_unlock(&router_lock); + return; + } else if (ret > 0) + next_router->flags |= COA_TENTATIVE; + + mdet_statemachine(RA_RCVD); /* TODO: What if DAD fails... */ + if (next_router->interval) + mod_timer(&r_timer, jiffies + + (next_router->interval * HZ)/1000); + else + mod_timer(&r_timer, jiffies + max_rtr_reach_time * HZ); + + + if (ret == 0) { + ipv6_addr_copy(&coa, &next_router->CoA); + ifindex = next_router->ifindex; + spin_unlock(&router_lock); + mipv6_mdet_finalize_ho(&coa, ifindex); + return; + } + spin_unlock(&router_lock); + +} +static unsigned long ns_send(void) +{ + struct neighbour *neigh; + struct net_device *dev; + struct in6_addr *raddr; + + DEBUG(DBG_DATADUMP, "Sending Neighbour solicitation to default router to verify its reachability"); + if (!curr_router) + return HZ; + if ((dev = dev_get_by_index(curr_router->ifindex)) == NULL) + return HZ; + if ((neigh = ndisc_get_neigh(dev, &curr_router->ll_addr)) == NULL) { + dev_put(dev); + return HZ; + } + if (curr_router->glob_addr) + raddr = &curr_router->raddr; + else + raddr = &curr_router->ll_addr; + + curr_router->last_ns_sent = jiffies; + ndisc_send_ns(dev, neigh, raddr, raddr, NULL); + + neigh_release(neigh); + dev_put(dev); + return HZ/5; /* Wait 200ms for a reply */ +} + +static int na_rcvd(void) +{ + int neigh_ok = 0; + struct neighbour *neigh; + struct net_device *dev; + + if (!curr_router) + return 0; + if ((dev = dev_get_by_index(curr_router->ifindex)) == NULL) + return 0; + if ((neigh = ndisc_get_neigh(dev, &curr_router->ll_addr)) == NULL) { + dev_put(dev); + return 0; + } + if (neigh->flags & NTF_ROUTER && + (time_after(neigh->confirmed, curr_router->last_ns_sent) || + neigh->confirmed == curr_router->last_ns_sent)) { + neigh_ok = 1; + DEBUG(DBG_DATADUMP, "Mdetect event: NA rcvd from curr rtr"); + } else + DEBUG(DBG_DATADUMP, "Mdetect event: NA NOT rcvd from curr rtr within time limit"); + neigh_release(neigh); + dev_put(dev); + return neigh_ok; +} + +static void coa_timer_handler(unsigned long dummy) +{ + + spin_lock_bh(&ho_lock); + if (_ho) { + DEBUG(DBG_INFO, "Starting handoff after DAD"); + mipv6_mobile_node_moved(_ho); + kfree(_ho); + _ho = NULL; + } + spin_unlock_bh(&ho_lock); +} +static void timer_handler(unsigned long foo) +{ + unsigned long timeout; + int state; + spin_lock_bh(&router_lock); + + if (_curr_state != NO_RTR) + rs_state = START; + + if (_curr_state == RTR_SUSPECT && na_rcvd()) { + state = mdet_statemachine(NA_RCVD); + timeout = curr_router->interval ? curr_router->interval : max_rtr_reach_time * HZ; + } else { + state = mdet_statemachine(TIMEOUT); + if (state == NO_RTR) + timeout = rs_send(); + else /* RTR_SUSPECT */ + timeout = ns_send(); + } + if (!timeout) + timeout = HZ; + + mipv6_router_gc(); + mod_timer(&r_timer, jiffies + timeout); + spin_unlock_bh(&router_lock); +} + +/** + * mipv6_get_care_of_address - get node's care-of primary address + * @homeaddr: one of node's home addresses + * @coaddr: buffer to store care-of address + * + * Stores the current care-of address in the @coaddr, assumes + * addresses in EUI-64 format. Since node might have several home + * addresses caller MUST supply @homeaddr. If node is at home + * @homeaddr is stored in @coaddr. Returns 0 on success, otherwise a + * negative value. + **/ +int mipv6_get_care_of_address( + struct in6_addr *homeaddr, struct in6_addr *coaddr) +{ + + DEBUG_FUNC(); + + if (homeaddr == NULL) + return -1; + spin_lock_bh(&router_lock); + if (curr_router == NULL || mipv6_mn_is_at_home(homeaddr) || + mipv6_prefix_compare(homeaddr, &curr_router->raddr, 64) || + curr_router->flags&COA_TENTATIVE) { + DEBUG(DBG_INFO, + "mipv6_get_care_of_address: returning home address"); + ipv6_addr_copy(coaddr, homeaddr); + spin_unlock_bh(&router_lock); + return 0; + + } + + /* At home or address check failure probably due to dad wait */ + if (mipv6_prefix_compare(&curr_router->raddr, homeaddr, + curr_router->pfix_len) + || (dad == RESPECT_DAD && + (ipv6_chk_addr(coaddr, NULL) == 0))) { + ipv6_addr_copy(coaddr, homeaddr); + } else { + ipv6_addr_copy(coaddr, &curr_router->CoA); + } + + spin_unlock_bh(&router_lock); + return 0; +} + +int mipv6_mdet_del_if(int ifindex) +{ + struct router *curr = NULL; + struct list_head *lh, *lh_tmp; + + spin_lock_bh(&router_lock); + list_for_each_safe(lh, lh_tmp, &rtr_list) { + curr = list_entry(lh, struct router, list); + if (curr->ifindex == ifindex) { + num_routers--; + list_del_init(&curr->list); + DEBUG(DBG_DATADUMP, "Deleting router %x:%x:%x:%x:%x:%x:%x:%x on interface %d", + NIPV6ADDR(&curr->raddr), ifindex); + if (curr_router == curr) + curr_router = NULL; + kfree(curr); + } + } + spin_unlock_bh(&router_lock); + return 0; +} + +void mipv6_mdet_retrigger_ho(void) +{ + struct handoff ho; + + spin_lock_bh(&router_lock); + if (curr_router != NULL) { + ho.coa = &curr_router->CoA; + ho.plen = curr_router->pfix_len; + ho.ifindex = curr_router->ifindex; + ipv6_addr_copy(&ho.rtr_addr, &curr_router->raddr); + ho.home_address = (curr_router->glob_addr && + curr_router->flags&ND_RA_FLAG_HA); + } + spin_unlock_bh(&router_lock); + mipv6_mobile_node_moved(&ho); +} + +void mipv6_mdet_set_curr_rtr_reachable(int reachable) +{ + spin_lock_bh(&router_lock); + if (curr_router != NULL) { + curr_router->reachable = reachable; + } + spin_unlock_bh(&router_lock); + +} + +int mipv6_mdet_finalize_ho(const struct in6_addr *coa, const int ifindex) +{ + int dummy; + struct handoff ho; + struct router *tmp; + struct net_device *dev; + struct in6_addr ll_addr; + + spin_lock_bh(&router_lock); + + if (!next_router) { + spin_unlock_bh(&router_lock); + return 0; + } + + dev = dev_get_by_index(next_router->ifindex); + + if (ipv6_get_lladdr(dev, &ll_addr) == 0) { + if (ipv6_addr_cmp(&ll_addr, coa) == 0) + DEBUG(DBG_INFO, "DAD for link local address completed"); + next_router->flags &= ~LLADDR_TENTATIVE; + } + + dev_put(dev); + + if (mipv6_prefix_compare(coa, &next_router->CoA, + next_router->pfix_len)) { + DEBUG(DBG_INFO, "DAD for Care-of address completed"); + next_router->flags &= ~COA_TENTATIVE; + } + if (!(next_router->flags&LLADDR_TENTATIVE) && !(next_router->flags&COA_TENTATIVE)) { + DEBUG(DBG_INFO, "%s: Proceeding with handoff after DAD\n", __FUNCTION__); + tmp = curr_router; + curr_router = next_router; + curr_router->is_current = 1; + next_router = NULL; + curr_router->flags &= ~COA_TENTATIVE; + delete_routes(curr_router); + delete_coas(curr_router); + if (tmp) { + struct net_device *dev_old = dev_get_by_index(tmp->ifindex); + struct rt6_info *rt = NULL; + if (dev_old) { + rt = rt6_get_dflt_router(&tmp->ll_addr, dev_old); + dev_put(dev_old); + } + if (rt) + ip6_del_rt(rt, NULL); + tmp->is_current = 0; + } + + ma_ctl_upd_iface(curr_router->ifindex, MA_IFACE_CURRENT, &dummy); + ma_ctl_upd_iface(curr_router->ifindex, MA_IFACE_CURRENT, &dummy); + + + ho.coa = &curr_router->CoA; + ho.plen = curr_router->pfix_len; + ho.ifindex = curr_router->ifindex; + ipv6_addr_copy(&ho.rtr_addr, &curr_router->raddr); + ho.home_address = (curr_router->glob_addr && + curr_router->flags&ND_RA_FLAG_HA); + + spin_unlock_bh(&router_lock); + mipv6_mobile_node_moved(&ho); + } else + spin_unlock_bh(&router_lock); + return 0; +} +/* Decides whether router candidate is the same router as current rtr + * based on prefix / global addresses of the routers and their link local + * addresses + */ +static int is_current_rtr(struct router *nrt, struct router *crt) +{ + DEBUG_FUNC(); + + DEBUG(DEBUG_MDETECT, "Current router: " + "%x:%x:%x:%x:%x:%x:%x:%x and", NIPV6ADDR(&crt->raddr)); + DEBUG(DEBUG_MDETECT, "Candidate router: " + "%x:%x:%x:%x:%x:%x:%x:%x", NIPV6ADDR(&nrt->raddr)); + + return (!ipv6_addr_cmp(&nrt->raddr,&crt->raddr) && + !ipv6_addr_cmp(&nrt->ll_addr, &crt->ll_addr)); +} + +/* + * Change next router to nrtr + * Returns 1, if router has been changed. + */ + +static int change_next_rtr(struct router *nrtr, struct router *ortr) +{ + int changed = 0; + DEBUG_FUNC(); + + if (!next_router || ipv6_addr_cmp(&nrtr->raddr, &next_router->raddr)) { + changed = 1; + } + next_router = nrtr; + return changed; +} +static int clean_ncache(struct router *nrt, struct router *ort, int same_if) +{ + struct net_device *ortdev; + DEBUG_FUNC(); + + /* Always call ifdown after a handoff to ensure proper routing */ + + if (!ort) + return 0; + if ((ortdev = dev_get_by_index(ort->ifindex)) == NULL) { + DEBUG(DBG_WARNING, "Device is not present"); + return -1; + } + neigh_ifdown(&nd_tbl, ortdev); + dev_put(ortdev); + return 0; +} + +static int mdet_get_if_preference(int ifi) +{ + int pref = 0; + + DEBUG_FUNC(); + + pref = ma_ctl_get_preference(ifi); + + DEBUG(DEBUG_MDETECT, "ifi: %d preference %d", ifi, pref); + + return pref; +} + +/* + * Called from mipv6_mn_ra_rcv to determine whether to do a handoff. + */ +static int mipv6_router_event(struct router *rptr) +{ + struct router *nrt = NULL; + int new_router = 0, same_if = 1; + int oldstate = _curr_state; + int addrtype = ipv6_addr_type(&rptr->raddr); + + DEBUG_FUNC(); + + if (rptr->lifetime == 0) + return MIPV6_IGN_RTR; + DEBUG(DEBUG_MDETECT, "Received a RA from router: " + "%x:%x:%x:%x:%x:%x:%x:%x", NIPV6ADDR(&rptr->raddr)); + spin_lock(&router_lock); + + /* Add or update router entry */ + if ((nrt = mipv6_rtr_get(&rptr->raddr)) == NULL) { + if (addrtype == IPV6_ADDR_ANY || (nrt = mipv6_rtr_add(rptr)) == NULL) { + spin_unlock(&router_lock); + return MIPV6_IGN_RTR; + } + DEBUG(DBG_INFO, "Router not on list,adding it to the list"); + new_router = 1; + } + nrt->last_ra_rcvd = jiffies; + nrt->state = ROUTER_REACHABLE; + nrt->interval = rptr->interval; + nrt->lifetime = rptr->lifetime; + nrt->ifindex = rptr->ifindex; + nrt->flags = rptr->flags; + nrt->glob_addr = rptr->glob_addr; + + /* Whether from current router */ + if (curr_router && curr_router->reachable && + is_current_rtr(nrt, curr_router)) { + if (nrt->interval) + mod_timer(&r_timer, jiffies + (nrt->interval * HZ)/1000); + else + mod_timer(&r_timer, jiffies + max_rtr_reach_time * HZ); + mdet_statemachine(RA_RCVD); + spin_unlock(&router_lock); + return MIPV6_ADD_RTR; + } else if (oldstate == NO_RTR) { + rt6_purge_dflt_routers(0); /* For multiple interface case */ + DEBUG(DBG_INFO, "No router or router not reachable, switching to new one"); + goto handoff; + } + if (!curr_router) { + /* Startup */ + goto handoff; + } + /* Router behind same interface as current one ?*/ + same_if = (nrt->ifindex == curr_router->ifindex); + /* Switch to new router behind same interface if eager cell + * switching is used or if the interface is preferred + */ + if ((new_router && eager_cell_switching && same_if) || + (mdet_get_if_preference(nrt->ifindex) > + mdet_get_if_preference(curr_router->ifindex))) { + DEBUG(DBG_INFO, "Switching to new router."); + goto handoff; + } + + /* No handoff, don't add default route */ + DEBUG(DEBUG_MDETECT, "Ignoring RA"); + spin_unlock(&router_lock); + return MIPV6_IGN_RTR; +handoff: + clean_ncache(nrt, curr_router, same_if); + nrt->reachable = 1; + if (same_if && change_next_rtr(nrt, curr_router)) { + mipv6_do_ll_dad(nrt->ifindex); + nrt->flags |= LLADDR_TENTATIVE; + } + spin_unlock(&router_lock); + + return MIPV6_CHG_RTR; +} + +/* + * Called from ndisc.c's router_discovery. + */ + +static inline int ret_to_ha(struct in6_addr *addr) +{ + int res = 0; + struct mn_info *minfo; + read_lock(&mn_info_lock); + minfo = mipv6_mninfo_get_by_ha(addr); + if (minfo != NULL) { + spin_lock(&minfo->lock); + if (minfo->has_home_reg) { + res = 1; + } + spin_unlock(&minfo->lock); + } + read_unlock(&mn_info_lock); + return res; +} + +static int mipv6_mn_ra_rcv(struct sk_buff *skb, struct ndisc_options *ndopts) +{ + int ifi = ((struct inet6_skb_parm *)skb->cb)->iif; + struct ra_msg *ra = (struct ra_msg *) skb->h.raw; + struct in6_addr *saddr = &skb->nh.ipv6h->saddr; + struct router nrt; + struct in6_addr *ha = NULL; + u8 *lladdr = NULL; + int res; + DEBUG_FUNC(); + + memset(&nrt, 0, sizeof(struct router)); + + if (ra->icmph.icmp6_home_agent) { + nrt.flags |= ND_RA_FLAG_HA; + DEBUG(DBG_DATADUMP, "RA has ND_RA_FLAG_HA up"); + } + + if (ra->icmph.icmp6_addrconf_managed) { + nrt.flags |= ND_RA_FLAG_MANAGED; + DEBUG(DBG_DATADUMP, "RA has ND_RA_FLAG_MANAGED up"); + } + + if (ra->icmph.icmp6_addrconf_other) { + nrt.flags |= ND_RA_FLAG_OTHER; + DEBUG(DBG_DATADUMP, "RA has ND_RA_FLAG_OTHER up"); + } + + ipv6_addr_copy(&nrt.ll_addr, saddr); + nrt.ifindex = ifi; + nrt.lifetime = ntohs(ra->icmph.icmp6_rt_lifetime); + + if (ndopts->nd_opts_src_lladdr) { + lladdr = (u8 *) ndopts->nd_opts_src_lladdr+2; + nrt.link_addr_len = skb->dev->addr_len; + memcpy(nrt.link_addr, lladdr, nrt.link_addr_len); + } + if (ndopts->nd_opts_pi) { + struct nd_opt_hdr *p; + for (p = ndopts->nd_opts_pi; + p; + p = ndisc_next_option(p, ndopts->nd_opts_pi_end)) { + struct prefix_info *pinfo; + int update = 0; + + pinfo = (struct prefix_info *) p; + + if (!pinfo->autoconf) + continue; + + if ((pinfo->router_address && + (update = ret_to_ha(&pinfo->prefix))) || + ipv6_addr_type(&nrt.raddr) != IPV6_ADDR_UNICAST) { + ipv6_addr_copy(&nrt.raddr, &pinfo->prefix); + nrt.pfix_len = pinfo->prefix_len; + if (pinfo->router_address) + nrt.glob_addr = 1; + else + nrt.glob_addr = 0; + if (update) + ha = &pinfo->prefix; + DEBUG(DBG_DATADUMP, "Address of the received " + "prefix info option: %x:%x:%x:%x:%x:%x:%x:%x", + NIPV6ADDR(&nrt.raddr)); + DEBUG(DBG_DATADUMP, "the length of the prefix is %d", + nrt.pfix_len); + } + } + } + if (ndopts->nd_opts_rai) { + nrt.interval = ntohl(*(__u32 *)(ndopts->nd_opts_rai+4)); + DEBUG(DBG_DATADUMP, + "received router interval option with interval : %d ", + nrt.interval / HZ); + + if (nrt.interval > MAX_RADV_INTERVAL) { + nrt.interval = 0; + DEBUG(DBG_DATADUMP, "but we are using: %d, " + "because interval>MAX_RADV_INTERVAL", + nrt.interval / HZ); + } + } + + res = mipv6_router_event(&nrt); + + if (ha && lladdr) { + mipv6_mn_ha_nd_update(__dev_get_by_index(ifi), ha, lladdr); + } + return res; +} + +int __init mipv6_initialize_mdetect(void) +{ + + DEBUG_FUNC(); + + spin_lock_init(&router_lock); + spin_lock_init(&ho_lock); + init_timer(&coa_timer); + init_timer(&r_timer); + r_timer.expires = jiffies + HZ; + add_timer(&r_timer); + + /* Actual HO, also deletes old routes after the addition of new ones + in ndisc */ + MIPV6_SETCALL(mipv6_change_router, mipv6_change_router); + + MIPV6_SETCALL(mipv6_ra_rcv, mipv6_mn_ra_rcv); + + return 0; +} + +int __exit mipv6_shutdown_mdetect() +{ + + DEBUG_FUNC(); + + MIPV6_RESETCALL(mipv6_ra_rcv); + MIPV6_RESETCALL(mipv6_change_router); + spin_lock_bh(&router_lock); + spin_lock(&ho_lock); + del_timer(&coa_timer); + del_timer(&r_timer); + /* Free the memory allocated by router list */ + list_free(&curr_router); + if (_ho) + kfree(_ho); + spin_unlock(&ho_lock); + spin_unlock_bh(&router_lock); + return 0; +} diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/mdetect.h linux-2.4.25/net/ipv6/mobile_ip6/mdetect.h --- linux-2.4.25.old/net/ipv6/mobile_ip6/mdetect.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.25/net/ipv6/mobile_ip6/mdetect.h 2004-06-26 11:29:31.000000000 +0100 @@ -0,0 +1,37 @@ +/* + * MIPL Mobile IPv6 Movement detection module header file + * + * $Id$ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _MDETECT_H +#define _MDETECT_H + +struct handoff { + int home_address; /* Is the coa a home address */ + int ifindex; + int plen; + struct in6_addr *coa; + struct in6_addr rtr_addr; /* Prefix or rtr address if coa is home address */ +}; + +int mipv6_initialize_mdetect(void); + +int mipv6_shutdown_mdetect(void); + +int mipv6_get_care_of_address(struct in6_addr *homeaddr, struct in6_addr *coa); + +int mipv6_mdet_del_if(int ifindex); + +int mipv6_mdet_finalize_ho(const struct in6_addr *coa, const int ifindex); + +void mipv6_mdet_retrigger_ho(void); + +void mipv6_mdet_set_curr_rtr_reachable(int reachable); + +#endif /* _MDETECT_H */ diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/mipv6_icmp.c linux-2.4.25/net/ipv6/mobile_ip6/mipv6_icmp.c --- linux-2.4.25.old/net/ipv6/mobile_ip6/mipv6_icmp.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.25/net/ipv6/mobile_ip6/mipv6_icmp.c 2004-06-26 11:29:31.000000000 +0100 @@ -0,0 +1,342 @@ +/** + * Generic icmp routines + * + * Authors: + * Jaakko Laine , + * Ville Nuorvala + * + * $Id$ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "debug.h" +#include "bcache.h" +#include "mipv6_icmp.h" +#include "config.h" + +struct mipv6_icmpv6_msg { + struct icmp6hdr icmph; + __u8 *data; + struct in6_addr *daddr; + int len; + __u32 csum; +}; + +#define MIPV6_ICMP_HOP_LIMIT 64 + +static struct socket *mipv6_icmpv6_socket = NULL; +static __u16 identifier = 0; + +int mipv6_icmpv6_no_rcv(struct sk_buff *skb) +{ + return 0; +} + +static int mipv6_icmpv6_xmit_holder = -1; + +static int mipv6_icmpv6_xmit_lock_bh(void) +{ + if (!spin_trylock(&mipv6_icmpv6_socket->sk->lock.slock)) { + if (mipv6_icmpv6_xmit_holder == smp_processor_id()) + return -EAGAIN; + spin_lock(&mipv6_icmpv6_socket->sk->lock.slock); + } + mipv6_icmpv6_xmit_holder = smp_processor_id(); + return 0; +} + +static __inline__ int mipv6_icmpv6_xmit_lock(void) +{ + int ret; + local_bh_disable(); + ret = mipv6_icmpv6_xmit_lock_bh(); + if (ret) + local_bh_enable(); + return ret; +} + +static void mipv6_icmpv6_xmit_unlock_bh(void) +{ + mipv6_icmpv6_xmit_holder = -1; + spin_unlock(&mipv6_icmpv6_socket->sk->lock.slock); +} + +static __inline__ void mipv6_icmpv6_xmit_unlock(void) +{ + mipv6_icmpv6_xmit_unlock_bh(); + local_bh_enable(); +} + + +/** + * mipv6_icmpv6_dest_unreach - Destination Unreachable ICMP error message handler + * @skb: buffer containing ICMP error message + * + * Special Mobile IPv6 ICMP handling. If Correspondent Node receives + * persistent ICMP Destination Unreachable messages for a destination + * in its Binding Cache, the binding should be deleted. See draft + * section 8.8. + **/ +static int mipv6_icmpv6_rcv_dest_unreach(struct sk_buff *skb) +{ + struct icmp6hdr *icmph = (struct icmp6hdr *) skb->h.raw; + struct ipv6hdr *ipv6h = (struct ipv6hdr *) (icmph + 1); + int left = (skb->tail - skb->h.raw) - sizeof(*icmph)- sizeof(ipv6h); + struct ipv6_opt_hdr *eh; + struct rt2_hdr *rt2h = NULL; + struct in6_addr *daddr = &ipv6h->daddr; + struct in6_addr *saddr = &ipv6h->saddr; + int hdrlen, nexthdr = ipv6h->nexthdr; + struct mipv6_bce bce; + DEBUG_FUNC(); + + eh = (struct ipv6_opt_hdr *) (ipv6h + 1); + + while (left > 0) { + if (nexthdr != NEXTHDR_HOP && nexthdr != NEXTHDR_DEST && + nexthdr != NEXTHDR_ROUTING) + return 0; + + hdrlen = ipv6_optlen(eh); + if (hdrlen > left) + return 0; + + if (nexthdr == NEXTHDR_ROUTING) { + struct ipv6_rt_hdr *rth = (struct ipv6_rt_hdr *) eh; + + if (rth->type == IPV6_SRCRT_TYPE_2) { + if (hdrlen != sizeof(struct rt2_hdr)) + return 0; + + rt2h = (struct rt2_hdr *) rth; + + if (rt2h->rt_hdr.segments_left > 0) + daddr = &rt2h->addr; + break; + } + } + /* check for home address option in case this node is a MN */ + if (nexthdr == NEXTHDR_DEST) { + __u8 *raw = (__u8 *) eh; + __u16 i = 2; + while (1) { + struct mipv6_dstopt_homeaddr *hao; + + if (i + sizeof (*hao) > hdrlen) + break; + + hao = (struct mipv6_dstopt_homeaddr *) &raw[i]; + + if (hao->type == MIPV6_TLV_HOMEADDR && + hao->length == sizeof(struct in6_addr)) { + saddr = &hao->addr; + break; + } + if (hao->type) + i += hao->length + 2; + else + i++; + } + + } + nexthdr = eh->nexthdr; + eh = (struct ipv6_opt_hdr *) ((u8 *) eh + hdrlen); + left -= hdrlen; + } + if (rt2h == NULL) return 0; + + if (mipv6_bcache_get(daddr, saddr, &bce) == 0 && !(bce.flags&HOME_REGISTRATION)) { + /* A primitive algorithm for detecting persistent ICMP destination unreachable messages */ + if (bce.destunr_count && + time_after(jiffies, + bce.last_destunr + MIPV6_DEST_UNR_IVAL*HZ)) + bce.destunr_count = 0; + + bce.destunr_count++; + + mipv6_bcache_icmp_err(daddr, saddr, bce.destunr_count); + + if (bce.destunr_count > MIPV6_MAX_DESTUNREACH && mipv6_bcache_delete(daddr, saddr, CACHE_ENTRY) == 0) { + DEBUG(DBG_INFO, "Deleted bcache entry " + "%x:%x:%x:%x:%x:%x:%x:%x " + "%x:%x:%x:%x:%x:%x:%x:%x (reason: " + "%d dest unreachables) ", + NIPV6ADDR(daddr), NIPV6ADDR(saddr), bce.destunr_count); + } + } + return 0; +} + +static int mipv6_icmpv6_getfrag(const void *data, struct in6_addr *saddr, + char *buff, unsigned int offset, + unsigned int len) +{ + struct mipv6_icmpv6_msg *msg = (struct mipv6_icmpv6_msg *) data; + struct icmp6hdr *icmph; + __u32 csum; + + if (offset) { + msg->csum = csum_partial_copy_nocheck(msg->data + offset - + sizeof(*icmph), buff, + len, msg->csum); + return 0; + } + + csum = csum_partial_copy_nocheck((__u8 *) &msg->icmph, buff, + sizeof(*icmph), msg->csum); + + csum = csum_partial_copy_nocheck(msg->data, buff + sizeof(*icmph), + len - sizeof(*icmph), csum); + + icmph = (struct icmp6hdr *) buff; + + icmph->icmp6_cksum = csum_ipv6_magic(saddr, msg->daddr, msg->len, + IPPROTO_ICMPV6, csum); + return 0; +} + +/** + * mipv6_icmpv6_send - generic icmpv6 message send + * @daddr: destination address + * @saddr: source address + * @type: icmp type + * @code: icmp code + * @id: packet identifier. If null, uses internal counter to get new id + * @data: packet data + * @datalen: length of data in bytes + */ +void mipv6_icmpv6_send(struct in6_addr *daddr, struct in6_addr *saddr, int type, + int code, __u16 *id, __u16 flags, void *data, int datalen) +{ + struct sock *sk = mipv6_icmpv6_socket->sk; + struct flowi fl; + struct mipv6_icmpv6_msg msg; + + DEBUG_FUNC(); + + fl.proto = IPPROTO_ICMPV6; + fl.fl6_dst = daddr; + fl.fl6_src = saddr; + fl.fl6_flowlabel = 0; + fl.uli_u.icmpt.type = type; + fl.uli_u.icmpt.code = code; + + msg.icmph.icmp6_type = type; + msg.icmph.icmp6_code = code; + msg.icmph.icmp6_cksum = 0; + + if (id) + msg.icmph.icmp6_identifier = htons(*id); + else + msg.icmph.icmp6_identifier = htons(identifier++); + + msg.icmph.icmp6_sequence = htons(flags); + msg.data = data; + msg.csum = 0; + msg.len = datalen + sizeof(struct icmp6hdr); + msg.daddr = daddr; + + if (mipv6_icmpv6_xmit_lock()) + return; + + ip6_build_xmit(sk, mipv6_icmpv6_getfrag, &msg, &fl, msg.len, NULL, -1, + MSG_DONTWAIT); + + ICMP6_INC_STATS_BH(Icmp6OutMsgs); + mipv6_icmpv6_xmit_unlock(); +} + +/** + * icmp6_rcv - ICMPv6 receive and multiplex + * @skb: buffer containing ICMP message + * + * Generic ICMPv6 receive function to multiplex messages to approriate + * handlers. Only used for ICMP messages with special handling in + * Mobile IPv6. + **/ +static void icmp6_rcv(struct sk_buff *skb) +{ + struct icmp6hdr *hdr; + + if (skb_is_nonlinear(skb) && + skb_linearize(skb, GFP_ATOMIC) != 0) { + kfree_skb(skb); + return; + } + __skb_push(skb, skb->data-skb->h.raw); + + hdr = (struct icmp6hdr *) skb->h.raw; + + switch (hdr->icmp6_type) { + case ICMPV6_DEST_UNREACH: + mipv6_icmpv6_rcv_dest_unreach(skb); + break; + + case ICMPV6_PARAMPROB: + mip6_fn.icmpv6_paramprob_rcv(skb); + break; + + case MIPV6_DHAAD_REPLY: + mip6_fn.icmpv6_dhaad_rep_rcv(skb); + break; + + case MIPV6_PREFIX_ADV: + mip6_fn.icmpv6_pfxadv_rcv(skb); + break; + + case MIPV6_DHAAD_REQUEST: + mip6_fn.icmpv6_dhaad_req_rcv(skb); + break; + + case MIPV6_PREFIX_SOLICIT: + mip6_fn.icmpv6_pfxsol_rcv(skb); + break; + } +} + +int mipv6_icmpv6_init(void) +{ + struct sock *sk; + int err; + + if ((mipv6_icmpv6_socket = sock_alloc()) == NULL) { + DEBUG(DBG_ERROR, "Cannot allocate mipv6_icmpv6_socket"); + return -1; + } + mipv6_icmpv6_socket->type = SOCK_RAW; + + if ((err = sock_create(PF_INET6, SOCK_RAW, IPPROTO_ICMP, + &mipv6_icmpv6_socket)) < 0) { + DEBUG(DBG_ERROR, "Cannot initialize mipv6_icmpv6_socket"); + sock_release(mipv6_icmpv6_socket); + mipv6_icmpv6_socket = NULL; /* For safety */ + return err; + } + sk = mipv6_icmpv6_socket->sk; + sk->allocation = GFP_ATOMIC; + sk->prot->unhash(sk); + + /* Register our ICMP handler */ + MIPV6_SETCALL(mipv6_icmp_rcv, icmp6_rcv); + return 0; +} + +void mipv6_icmpv6_exit(void) +{ + MIPV6_RESETCALL(mipv6_icmp_rcv); + if (mipv6_icmpv6_socket) + sock_release(mipv6_icmpv6_socket); + mipv6_icmpv6_socket = NULL; /* For safety */ +} diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/mipv6_icmp.h linux-2.4.25/net/ipv6/mobile_ip6/mipv6_icmp.h --- linux-2.4.25.old/net/ipv6/mobile_ip6/mipv6_icmp.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.25/net/ipv6/mobile_ip6/mipv6_icmp.h 2004-06-26 11:29:31.000000000 +0100 @@ -0,0 +1,43 @@ +/* + * MIPL Mobile IPv6 ICMP send and receive prototypes + * + * $Id$ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _MIPV6_ICMP +#define _MIPV6_ICMP + +#include +#include + +void mipv6_icmpv6_send(struct in6_addr *daddr, struct in6_addr *saddr, + int type, int code, __u16 *id, __u16 flags, + void *data, int datalen); + +void mipv6_icmpv6_send_dhaad_req(struct in6_addr *home_addr, int plen, __u16 dhaad_id); + +void mipv6_icmpv6_send_dhaad_rep(int ifindex, __u16 id, struct in6_addr *daddr); +/* No handling */ +int mipv6_icmpv6_no_rcv(struct sk_buff *skb); + +/* Receive DHAAD Reply message */ +int mipv6_icmpv6_rcv_dhaad_rep(struct sk_buff *skb); +/* Receive Parameter Problem message */ +int mipv6_icmpv6_rcv_paramprob(struct sk_buff *skb); +/* Receive prefix advertisements */ +int mipv6_icmpv6_rcv_pfx_adv(struct sk_buff *skb); + +/* Receive DHAAD Request message */ +int mipv6_icmpv6_rcv_dhaad_req(struct sk_buff *skb); +/* Receive prefix solicitations */ +int mipv6_icmpv6_rcv_pfx_sol(struct sk_buff *skb); + +int mipv6_icmpv6_init(void); +void mipv6_icmpv6_exit(void); + +#endif diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/mipv6_icmp_ha.c linux-2.4.25/net/ipv6/mobile_ip6/mipv6_icmp_ha.c --- linux-2.4.25.old/net/ipv6/mobile_ip6/mipv6_icmp_ha.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.25/net/ipv6/mobile_ip6/mipv6_icmp_ha.c 2004-06-26 11:29:31.000000000 +0100 @@ -0,0 +1,158 @@ +/* + * Home Agent specific ICMP routines + * + * Authors: + * Antti Tuominen + * Jaakko Laine + * + * $Id$ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include + +#include "halist.h" +#include "debug.h" +#include "mipv6_icmp.h" +//#include "prefix.h" + +/* Is this the easiest way of checking on + * which interface an anycast address is ? + */ +static int find_ac_dev(struct in6_addr *addr) +{ + int ifindex = 0; + struct net_device *dev; + read_lock(&dev_base_lock); + for (dev=dev_base; dev; dev=dev->next) { + if (ipv6_chk_acast_addr(dev, addr)) { + ifindex = dev->ifindex; + break; + } + } + read_unlock(&dev_base_lock); + return ifindex; +} + +/** + * mipv6_icmpv6_send_dhaad_rep - Reply to DHAAD Request + * @ifindex: index of interface request was received from + * @id: request's identification number + * @daddr: requester's IPv6 address + * + * When Home Agent receives Dynamic Home Agent Address Discovery + * request, it replies with a list of home agents available on the + * home link. + */ +void mipv6_icmpv6_send_dhaad_rep(int ifindex, __u16 id, struct in6_addr *daddr) +{ + __u8 *data = NULL; + struct in6_addr home, *ha_addrs = NULL; + int addr_count, max_addrs, size = 0; + + if (daddr == NULL) + return; + + if (mipv6_ha_get_addr(ifindex, &home) < 0) { + DEBUG(DBG_INFO, "Not Home Agent in this interface"); + return; + } + + /* We send all available HA addresses, not exceeding a maximum + * number we can fit in a packet with minimum IPv6 MTU (to + * avoid fragmentation). + */ + max_addrs = 76; + addr_count = mipv6_ha_get_pref_list(ifindex, &ha_addrs, max_addrs); + + if (addr_count < 0) return; + + if (addr_count != 0 && ha_addrs == NULL) { + DEBUG(DBG_ERROR, "addr_count = %d but return no addresses", + addr_count); + return; + } + data = (u8 *)ha_addrs; + + size = addr_count * sizeof(struct in6_addr); + + mipv6_icmpv6_send(daddr, &home, MIPV6_DHAAD_REPLY, + 0, &id, 0, data, size); + if (ha_addrs) { + data = NULL; + kfree(ha_addrs); + } +} + +/** + * mipv6_icmpv6_dhaad_req - Home Agent Address Discovery Request ICMP handler + * @skb: buffer containing ICMP information message + * + * Special Mobile IPv6 ICMP message. Handles Dynamic Home Agent + * Address Discovery Request messages. + **/ +int mipv6_icmpv6_rcv_dhaad_req(struct sk_buff *skb) +{ + struct icmp6hdr *phdr = (struct icmp6hdr *) skb->h.raw; + struct in6_addr *saddr = &skb->nh.ipv6h->saddr; + struct in6_addr *daddr = &skb->nh.ipv6h->daddr; + __u16 identifier; + int ifindex = 0; + + DEBUG_FUNC(); + + /* Invalid packet checks. */ + if (phdr->icmp6_code != 0) + return 0; + + identifier = ntohs(phdr->icmp6_identifier); + + /* + * Make sure we have the right ifindex (if the + * req came through another interface. + */ + ifindex = find_ac_dev(daddr); + if (ifindex == 0) { + DEBUG(DBG_WARNING, "received dhaad request to anycast address %x:%x:%x:%x:%x:%x:%x:%x" + " on which prefix we are not HA", + NIPV6ADDR(daddr)); + return 0; + } + + /* + * send reply with list + */ + mipv6_icmpv6_send_dhaad_rep(ifindex, identifier, saddr); + return 1; +} +#if 0 +/** + * mipv6_icmpv6_handle_pfx_sol - handle prefix solicitations + * @skb: sk_buff including the icmp6 message + */ +int mipv6_icmpv6_rcv_pfx_sol(struct sk_buff *skb) +{ + struct in6_addr *saddr = &skb->nh.ipv6h->saddr; + struct in6_addr *daddr = &skb->nh.ipv6h->daddr; + struct inet6_ifaddr *ifp; + + DEBUG_FUNC(); + + if (!(ifp = ipv6_get_ifaddr(daddr, NULL))) + return -1; + + in6_ifa_put(ifp); + mipv6_pfx_cancel_send(saddr, -1); + + return 0; +} +#endif diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/mipv6_icmp_mn.c linux-2.4.25/net/ipv6/mobile_ip6/mipv6_icmp_mn.c --- linux-2.4.25.old/net/ipv6/mobile_ip6/mipv6_icmp_mn.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.25/net/ipv6/mobile_ip6/mipv6_icmp_mn.c 2004-06-26 11:29:31.000000000 +0100 @@ -0,0 +1,273 @@ +/* + * Mobile Node specific ICMP routines + * + * Authors: + * Antti Tuominen + * Jaakko Laine + * + * $Id$ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include + +#include "mn.h" +#include "bul.h" +#include "mdetect.h" +#include "debug.h" +#include "mipv6_icmp.h" +#include "util.h" +//#include "prefix.h" + +#define INFINITY 0xffffffff + +/** + * mipv6_icmpv6_paramprob - Parameter Problem ICMP error message handler + * @skb: buffer containing ICMP error message + * + * Special Mobile IPv6 ICMP handling. If Mobile Node receives ICMP + * Parameter Problem message when using a Home Address Option, + * offending node should be logged and error message dropped. If + * error is received because of a Binding Update, offending node + * should be recorded in Binding Update List and no more Binding + * Updates should be sent to this destination. See RFC 3775 section + * 10.15. + **/ +int mipv6_icmpv6_rcv_paramprob(struct sk_buff *skb) +{ + struct icmp6hdr *phdr = (struct icmp6hdr *) skb->h.raw; + struct in6_addr *saddr = skb ? &skb->nh.ipv6h->saddr : NULL; + struct in6_addr *daddr = skb ? &skb->nh.ipv6h->daddr : NULL; + struct ipv6hdr *hdr = (struct ipv6hdr *) (phdr + 1); + int ulen = (skb->tail - (unsigned char *) (phdr + 1)); + + int errptr; + __u8 *off_octet; + + DEBUG_FUNC(); + + /* We only handle code 1 & 2 messages. */ + if (phdr->icmp6_code != ICMPV6_UNK_NEXTHDR && + phdr->icmp6_code != ICMPV6_UNK_OPTION) + return 0; + + /* Find offending octet in the original packet. */ + errptr = ntohl(phdr->icmp6_pointer); + + /* There is not enough of the original packet left to figure + * out what went wrong. Bail out. */ + if (ulen <= errptr) + return 0; + + off_octet = ((__u8 *) hdr + errptr); + DEBUG(DBG_INFO, "Parameter problem: offending octet %d [0x%2x]", + errptr, *off_octet); + + /* If CN did not understand Mobility Header, set BUL entry to + * ACK_ERROR so no further BUs are sumbitted to this CN. */ + if (phdr->icmp6_code == ICMPV6_UNK_NEXTHDR && + *off_octet == IPPROTO_MOBILITY) { + struct bul_inval_args args; + args.all_rr_states = 1; + args.cn = saddr; + args.mn = daddr; + write_lock(&bul_lock); + mipv6_bul_iterate(mn_bul_invalidate, &args); + write_unlock(&bul_lock); + } + + /* If CN did not understand Home Address Option, we log an + * error and discard the error message. */ + if (phdr->icmp6_code == ICMPV6_UNK_OPTION && + *off_octet == MIPV6_TLV_HOMEADDR) { + DEBUG(DBG_WARNING, "Correspondent node does not " + "implement Home Address Option receipt."); + return 1; + } + return 0; +} + +/** + * mipv6_mn_dhaad_send_req - Send DHAAD Request to home network + * @home_addr: address to do DHAAD for + * @plen: prefix length for @home_addr + * + * Send Dynamic Home Agent Address Discovery Request to the Home + * Agents anycast address in the nodes home network. + **/ +void +mipv6_icmpv6_send_dhaad_req(struct in6_addr *home_addr, int plen, __u16 dhaad_id) +{ + struct in6_addr ha_anycast; + struct in6_addr careofaddr; + + if (mipv6_get_care_of_address(home_addr, &careofaddr) < 0) { + DEBUG(DBG_WARNING, "Could not get node's Care-of Address"); + return; + } + + if (mipv6_ha_anycast(&ha_anycast, home_addr, plen) < 0) { + DEBUG(DBG_WARNING, + "Could not get Home Agent Anycast address for home address %x:%x.%x:%x:%x:%x:%x:%x/%d", + NIPV6ADDR(home_addr), plen); + return; + } + + mipv6_icmpv6_send(&ha_anycast, &careofaddr, MIPV6_DHAAD_REQUEST, 0, + &dhaad_id, 0, NULL, 0); + +} + +/** + * mipv6_icmpv6_dhaad_rep - Home Agent Address Discovery Reply ICMP handler + * @skb: buffer containing ICMP information message + * + * Special Mobile IPv6 ICMP message. Handles Dynamic Home Agent + * Address Discovery Reply messages. + **/ +int mipv6_icmpv6_rcv_dhaad_rep(struct sk_buff *skb) +{ + struct icmp6hdr *phdr = (struct icmp6hdr *) skb->h.raw; + struct in6_addr *address; + struct in6_addr *saddr = &skb->nh.ipv6h->saddr; + __u16 identifier; + int ulen = (skb->tail - (unsigned char *) ((__u32 *) phdr + 2)); + int i; + struct in6_addr home_addr, coa; + struct in6_addr *first_ha = NULL; + struct mn_info *minfo; + int n_addr = ulen / sizeof(struct in6_addr); + + DEBUG_FUNC(); + + /* Invalid packet checks. */ + if (ulen % sizeof(struct in6_addr) != 0) + return 0; + + if (phdr->icmp6_code != 0) + return 0; + + identifier = ntohs(phdr->icmp6_identifier); + if (ulen > 0) { + address = (struct in6_addr *) ((__u32 *) phdr + 2); + } else { + address = saddr; + n_addr = 1; + } + + /* receive list of home agent addresses + * add to home agents list + */ + DEBUG(DBG_INFO, "DHAAD: got %d home agents", n_addr); + + first_ha = address; + + /* lookup H@ with identifier */ + read_lock(&mn_info_lock); + minfo = mipv6_mninfo_get_by_id(identifier); + if (!minfo) { + read_unlock(&mn_info_lock); + DEBUG(DBG_INFO, "no mninfo with id %d", + identifier); + return 0; + } + spin_lock(&minfo->lock); + + /* Logic: + * 1. if old HA on list, prefer it + * 2. otherwise first HA on list prefered + */ + for (i = 0; i < n_addr; i++) { + DEBUG(DBG_INFO, "HA[%d] %x:%x:%x:%x:%x:%x:%x:%x", + i, NIPV6ADDR(address)); + if (ipv6_addr_cmp(&minfo->ha, address) == 0) { + spin_unlock(&minfo->lock); + read_unlock(&mn_info_lock); + return 0; + } + address++; + } + ipv6_addr_copy(&minfo->ha, first_ha); + spin_unlock(&minfo->lock); + ipv6_addr_copy(&home_addr, &minfo->home_addr); + read_unlock(&mn_info_lock); + + mipv6_get_care_of_address(&home_addr, &coa); + init_home_registration(&home_addr, &coa); + + return 1; +} +#if 0 +/** + * mipv6_icmpv6_handle_pfx_adv - handle prefix advertisements + * @skb: sk_buff including the icmp6 message + */ +int mipv6_icmpv6_rcv_pfx_adv(struct sk_buff *skb) +{ + struct icmp6hdr *hdr = (struct icmp6hdr *) skb->h.raw; + struct in6_addr *saddr = &skb->nh.ipv6h->saddr; + struct in6_addr *daddr = &skb->nh.ipv6h->daddr; + __u8 *opt = (__u8 *) (hdr + 1); + int optlen = (skb->tail - opt); + unsigned long min_expire = INFINITY; + struct inet6_skb_parm *parm = (struct inet6_skb_parm *) skb->cb; + + DEBUG_FUNC(); + + while (optlen > 0) { + int len = opt[1] << 3; + if (len == 0) + goto set_timer; + + if (opt[0] == ND_OPT_PREFIX_INFO) { + int ifindex; + unsigned long expire; + struct prefix_info *pinfo = + (struct prefix_info *) opt; + struct net_device *dev; + struct mn_info *mninfo; + + read_lock(&mn_info_lock); + mninfo = mipv6_mninfo_get_by_ha(saddr); + if (mninfo == NULL) { + ifindex = 0; + } else { + spin_lock(&mninfo->lock); + ifindex = mninfo->ifindex; + spin_unlock(&mninfo->lock); + mninfo = NULL; + } + read_unlock(&mn_info_lock); + + if (!(dev = dev_get_by_index(ifindex))) { + DEBUG(DBG_WARNING, "Cannot find device by index %d", parm->iif); + goto nextopt; + } + + expire = ntohl(pinfo->valid); + expire = expire == 0 ? INFINITY : expire; + + min_expire = expire < min_expire ? expire : min_expire; + + dev_put(dev); + } + +nextopt: + optlen -= len; + opt += len; + } + +set_timer: + + mipv6_pfx_add_home(parm->iif, saddr, daddr, min_expire); + return 0; +} +#endif diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/mn.c linux-2.4.25/net/ipv6/mobile_ip6/mn.c --- linux-2.4.25.old/net/ipv6/mobile_ip6/mn.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.25/net/ipv6/mobile_ip6/mn.c 2004-06-26 11:29:31.000000000 +0100 @@ -0,0 +1,1521 @@ +/* + * Mobile-node functionality + * + * Authors: + * Sami Kivisaari + * + * $Id$ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include "util.h" +#include "mdetect.h" +#include "bul.h" +#include "mobhdr.h" +#include "debug.h" +#include "mn.h" +#include "mipv6_icmp.h" +#include "multiaccess_ctl.h" +//#include "prefix.h" +#include "tunnel_mn.h" +#include "stats.h" +#include "config.h" + +#define MIPV6_BUL_SIZE 128 + +static LIST_HEAD(mn_info_list); + +/* Lock for list of MN infos */ +rwlock_t mn_info_lock = RW_LOCK_UNLOCKED; + +static spinlock_t ifrh_lock = SPIN_LOCK_UNLOCKED; + +struct ifr_holder { + struct list_head list; + struct in6_ifreq ifr; + int old_ifi; + struct handoff *ho; +}; + +LIST_HEAD(ifrh_list); + +static struct tq_struct mv_home_addr_task; + +/* Determines whether manually configured home addresses are preferred as + * source addresses over dynamically configured ones + */ +int mipv6_use_preconfigured_hoaddr = 1; + +/* Determines whether home addresses, which are at home are preferred as + * source addresses over other home addresses + */ +int mipv6_use_topol_corr_hoaddr = 0; + +static spinlock_t icmpv6_id_lock = SPIN_LOCK_UNLOCKED; +static __u16 icmpv6_id = 0; + +static inline __u16 mipv6_get_dhaad_id(void) +{ + __u16 ret; + spin_lock_bh(&icmpv6_id_lock); + ret = ++icmpv6_id; + spin_unlock_bh(&icmpv6_id_lock); + return ret; +} + +/** + * mipv6_mninfo_get_by_home - Returns mn_info for a home address + * @haddr: home address of MN + * + * Returns mn_info on success %NULL otherwise. Caller MUST hold + * @mn_info_lock (read or write). + **/ +struct mn_info *mipv6_mninfo_get_by_home(struct in6_addr *haddr) +{ + struct list_head *lh; + struct mn_info *minfo; + + DEBUG_FUNC(); + + if (!haddr) + return NULL; + + list_for_each(lh, &mn_info_list) { + minfo = list_entry(lh, struct mn_info, list); + spin_lock(&minfo->lock); + if (!ipv6_addr_cmp(&minfo->home_addr, haddr)) { + spin_unlock(&minfo->lock); + return minfo; + } + spin_unlock(&minfo->lock); + } + return NULL; +} + +/** + * mipv6_mninfo_get_by_ha - Lookup mn_info with Home Agent address + * @home_agent: Home Agent address + * + * Searches for a mn_info entry with @ha set to @home_agent. You MUST + * hold @mn_info_lock when calling this function. Returns pointer to + * mn_info entry or %NULL on failure. + **/ +struct mn_info *mipv6_mninfo_get_by_ha(struct in6_addr *home_agent) +{ + struct list_head *lh; + struct mn_info *minfo; + + if (!home_agent) + return NULL; + + list_for_each(lh, &mn_info_list) { + minfo = list_entry(lh, struct mn_info, list); + spin_lock(&minfo->lock); + if (!ipv6_addr_cmp(&minfo->ha, home_agent)) { + spin_unlock(&minfo->lock); + return minfo; + } + spin_unlock(&minfo->lock); + } + return NULL; +} + +/** + * mipv6_mninfo_get_by_id - Lookup mn_info with id + * @id: DHAAD identifier + * + * Searches for a mn_info entry with @dhaad_id set to @id. You MUST + * hold @mn_info_lock when calling this function. Returns pointer to + * mn_info entry or %NULL on failure. + **/ +struct mn_info *mipv6_mninfo_get_by_id(unsigned short id) +{ + struct list_head *lh; + struct mn_info *minfo = 0; + + list_for_each(lh, &mn_info_list) { + minfo = list_entry(lh, struct mn_info, list); + spin_lock(&minfo->lock); + if (minfo->dhaad_id == id) { + spin_unlock(&minfo->lock); + return minfo; + } + spin_unlock(&minfo->lock); + } + return NULL; +} + +/** + * mipv6_mninfo_add - Adds a new home info for MN + * @ifindex: Interface for home address + * @home_addr: Home address of MN, must be set + * @plen: prefix length of the home address, must be set + * @isathome : home address at home + * @lifetime: lifetime of the home address, 0 is infinite + * @ha: home agent for the home address + * @ha_plen: prefix length of home agent's address, can be zero + * @ha_lifetime: Lifetime of the home address, 0 is infinite + * + * The function adds a new home info entry for MN, allowing it to + * register the home address with the home agent. Starts home + * registration process. If @ha is %ADDRANY, DHAAD is performed to + * find a home agent. Returns 0 on success, a negative value + * otherwise. Caller MUST NOT hold @mn_info_lock or + * @addrconf_hash_lock. + **/ +void mipv6_mninfo_add(int ifindex, struct in6_addr *home_addr, int plen, + int isathome, unsigned long lifetime, struct in6_addr *ha, + int ha_plen, unsigned long ha_lifetime, int man_conf) +{ + struct mn_info *minfo; + struct in6_addr coa; + + DEBUG_FUNC(); + + write_lock_bh(&mn_info_lock); + if ((minfo = mipv6_mninfo_get_by_home(home_addr)) != NULL){ + DEBUG(1, "MN info already exists"); + write_unlock_bh(&mn_info_lock); + return; + } + minfo = kmalloc(sizeof(struct mn_info), GFP_ATOMIC); + if (!minfo) { + write_unlock_bh(&mn_info_lock); + return; + } + memset(minfo, 0, sizeof(struct mn_info)); + spin_lock_init(&minfo->lock); + + + ipv6_addr_copy(&minfo->home_addr, home_addr); + + if (ha) + ipv6_addr_copy(&minfo->ha, ha); + if (ha_plen < 128 && ha_plen > 0) + minfo->home_plen = ha_plen; + else minfo->home_plen = 64; + + minfo->ifindex_user = ifindex; /* Ifindex for tunnel interface */ + minfo->ifindex = ifindex; /* Interface on which home address is currently conf'd */ + /* TODO: we should get home address lifetime from somewhere */ + /* minfo->home_addr_expires = jiffies + lifetime * HZ; */ + + /* manual configuration flag cannot be unset by dynamic updates + * from prefix advertisements + */ + if (!minfo->man_conf) minfo->man_conf = man_conf; + minfo->is_at_home = isathome; + + list_add(&minfo->list, &mn_info_list); + write_unlock_bh(&mn_info_lock); + + if (mipv6_get_care_of_address(home_addr, &coa) == 0) + init_home_registration(home_addr, &coa); +} + +/** + * mipv6_mninfo_del - Delete home info for MN + * @home_addr : Home address or prefix + * @del_dyn_only : Delete only dynamically created home entries + * + * Deletes every mn_info entry that matches the first plen bits of + * @home_addr. Returns number of deleted entries on success and a + * negative value otherwise. Caller MUST NOT hold @mn_info_lock. + **/ +int mipv6_mninfo_del(struct in6_addr *home_addr, int del_dyn_only) +{ + struct list_head *lh, *next; + struct mn_info *minfo; + int ret = -1; + if (!home_addr) + return -1; + + write_lock(&mn_info_lock); + + list_for_each_safe(lh, next, &mn_info_list) { + minfo = list_entry(lh, struct mn_info, list); + if (ipv6_addr_cmp(&minfo->home_addr, home_addr) == 0 + && ((!minfo->man_conf && del_dyn_only) || !del_dyn_only)){ + list_del(&minfo->list); + kfree(minfo); + ret++; + } + } + write_unlock(&mn_info_lock); + return ret; +} + +void mipv6_mn_set_home(int ifindex, struct in6_addr *homeaddr, int plen, + struct in6_addr *homeagent, int ha_plen) +{ + mipv6_mninfo_add(ifindex, homeaddr, plen, 0, 0, + homeagent, ha_plen, 0, 1); +} + +static int skip_dad(struct in6_addr *addr) +{ + struct mn_info *minfo; + int ret = 0; + + if (addr == NULL) { + DEBUG(DBG_CRITICAL, "Null argument"); + return 0; + } + read_lock_bh(&mn_info_lock); + if ((minfo = mipv6_mninfo_get_by_home(addr)) != NULL) { + if ((minfo->is_at_home != MN_NOT_AT_HOME) && (minfo->has_home_reg)) + ret = 1; + DEBUG(DBG_INFO, "minfo->is_at_home = %d, minfo->has_home_reg = %d", + minfo->is_at_home, minfo->has_home_reg); + } + read_unlock_bh(&mn_info_lock); + + return ret; +} +/** + * mipv6_mn_is_home_addr - Determines if addr is node's home address + * @addr: IPv6 address + * + * Returns 1 if addr is node's home address. Otherwise returns zero. + **/ +int mipv6_mn_is_home_addr(struct in6_addr *addr) +{ + int ret = 0; + + if (addr == NULL) { + DEBUG(DBG_CRITICAL, "Null argument"); + return -1; + } + read_lock_bh(&mn_info_lock); + if (mipv6_mninfo_get_by_home(addr)) + ret = 1; + read_unlock_bh(&mn_info_lock); + + return (ret); +} + +/** + * mipv6_mn_is_at_home - determine if node is home for a home address + * @home_addr : home address of MN + * + * Returns 1 if home address in question is in the home network, 0 + * otherwise. Caller MUST NOT not hold @mn_info_lock. + **/ +int mipv6_mn_is_at_home(struct in6_addr *home_addr) +{ + struct mn_info *minfo; + int ret = 0; + read_lock_bh(&mn_info_lock); + if ((minfo = mipv6_mninfo_get_by_home(home_addr)) != NULL) { + spin_lock(&minfo->lock); + ret = (minfo->is_at_home == MN_AT_HOME); + spin_unlock(&minfo->lock); + } + read_unlock_bh(&mn_info_lock); + return ret; +} +void mipv6_mn_set_home_reg(struct in6_addr *home_addr, int has_home_reg) +{ + struct mn_info *minfo; + read_lock_bh(&mn_info_lock); + + if ((minfo = mipv6_mninfo_get_by_home(home_addr)) != NULL) { + spin_lock(&minfo->lock); + minfo->has_home_reg = has_home_reg; + spin_unlock(&minfo->lock); + } + read_unlock_bh(&mn_info_lock); +} + +static int mn_inet6addr_event( + struct notifier_block *nb, unsigned long event, void *ptr) +{ + struct inet6_ifaddr *ifp = (struct inet6_ifaddr *)ptr; + + switch (event) { + case NETDEV_UP: + /* Is address a valid coa ?*/ + if (!(ifp->flags & IFA_F_TENTATIVE)) + mipv6_mdet_finalize_ho(&ifp->addr, + ifp->idev->dev->ifindex); + else if(skip_dad(&ifp->addr)) + ifp->flags &= ~IFA_F_TENTATIVE; + break; + case NETDEV_DOWN: +#if 0 + /* This is useless with manually configured home + addresses, which will not expire + */ + mipv6_mninfo_del(&ifp->addr, 0); +#endif + break; + + } + + return NOTIFY_DONE; +} + +struct notifier_block mipv6_mn_inet6addr_notifier = { + mn_inet6addr_event, + NULL, + 0 /* check if using zero is ok */ +}; + +static void mipv6_get_saddr_hook(struct in6_addr *homeaddr) +{ + int found = 0, reiter = 0; + struct list_head *lh; + struct mn_info *minfo = NULL; + struct in6_addr coa; + + read_lock_bh(&mn_info_lock); +restart: + list_for_each(lh, &mn_info_list) { + minfo = list_entry(lh, struct mn_info, list); + if ((ipv6_addr_scope(homeaddr) != ipv6_addr_scope(&minfo->home_addr)) + || ipv6_chk_addr(&minfo->home_addr, NULL) == 0) + continue; + + spin_lock(&minfo->lock); + if (minfo->is_at_home == MN_AT_HOME || minfo->has_home_reg) { + if ((mipv6_use_topol_corr_hoaddr && + minfo->is_at_home == MN_AT_HOME) || + (mipv6_use_preconfigured_hoaddr && + minfo->man_conf) || + (!(mipv6_use_preconfigured_hoaddr || + mipv6_use_topol_corr_hoaddr) || reiter)) { + spin_unlock(&minfo->lock); + ipv6_addr_copy(homeaddr, &minfo->home_addr); + found = 1; + break; + } + } + spin_unlock(&minfo->lock); + } + if (!found && !reiter) { + reiter = 1; + goto restart; + } + + if (!found && minfo && + !mipv6_get_care_of_address(&minfo->home_addr, &coa)) { + ipv6_addr_copy(homeaddr, &coa); + } + read_unlock_bh(&mn_info_lock); + + DEBUG(DBG_DATADUMP, "Source address selection: %x:%x:%x:%x:%x:%x:%x:%x", + NIPV6ADDR(homeaddr)); + return; +} + +static void mv_home_addr(void *arg) +{ + mm_segment_t oldfs; + int err = 0, new_if = 0; + struct list_head *lh, *next; + struct ifr_holder *ifrh; + LIST_HEAD(list); + + DEBUG(DBG_INFO, "mipv6 move home address task"); + + spin_lock_bh(&ifrh_lock); + list_splice_init(&ifrh_list, &list); + spin_unlock_bh(&ifrh_lock); + + oldfs = get_fs(); set_fs(KERNEL_DS); + list_for_each_safe(lh, next, &list) { + ifrh = list_entry(lh, struct ifr_holder, list); + if (ifrh->old_ifi) { + new_if = ifrh->ifr.ifr6_ifindex; + ifrh->ifr.ifr6_ifindex = ifrh->old_ifi; + err = addrconf_del_ifaddr(&ifrh->ifr); + ifrh->ifr.ifr6_ifindex = new_if; + if (err < 0) + DEBUG(DBG_WARNING, "removal of home address %x:%x:%x:%x:%x:%x:%x:%x from" + " old interface %d failed with status %d", + NIPV6ADDR(&ifrh->ifr.ifr6_addr), ifrh->old_ifi, err); + } + if(!err) { + err = addrconf_add_ifaddr(&ifrh->ifr); + } + if (ifrh->ho) { + DEBUG(DBG_INFO, "Calling mobile_node moved after moving home address to new if"); + mipv6_mobile_node_moved(ifrh->ho); + } + list_del(&ifrh->list); + kfree(ifrh); + } + set_fs(oldfs); + + if (err < 0) + DEBUG(DBG_WARNING, "adding of home address to a new interface %d failed %d", new_if, err); + else { + DEBUG(DBG_WARNING, "adding of home address to a new interface OK"); + } +} + +struct dhaad_halist { + struct list_head list; + struct in6_addr addr; + int retry; +}; + +/* clear all has from candidate list. do this when a new dhaad reply + * is received. */ +int mipv6_mn_flush_ha_candidate(struct list_head *ha) +{ + struct list_head *p, *tmp; + struct dhaad_halist *e; + + list_for_each_safe(p, tmp, ha) { + e = list_entry(p, struct dhaad_halist, list); + list_del(p); + kfree(e); + e = NULL; + } + return 0; +} + +/* add new ha to candidates. only done when dhaad reply is received. */ +int mipv6_mn_add_ha_candidate(struct list_head *ha, struct in6_addr *addr) +{ + struct dhaad_halist *e; + + e = kmalloc(sizeof(*e), GFP_ATOMIC); + memset(e, 0, sizeof(*e)); + ipv6_addr_copy(&e->addr, addr); + + list_add_tail(&e->list, ha); + return 0; +} + +#define MAX_RETRIES_PER_HA 3 + +/* get next ha candidate. this is done when dhaad reply has been + * received and we want to register with the best available ha. */ +int mipv6_mn_get_ha_candidate(struct list_head *ha, struct in6_addr *addr) +{ + struct list_head *p; + + list_for_each(p, ha) { + struct dhaad_halist *e; + e = list_entry(p, typeof(*e), list); + if (e->retry >= 0 && e->retry < MAX_RETRIES_PER_HA) { + ipv6_addr_copy(addr, &e->addr); + return 0; + } + } + return -1; +} + +/* change candidate status. if registration with ha fails, we + * increase retry for ha candidate. if retry is >= 3 we set it to -1 + * (failed), do get_ha_candidate() again */ +int mipv6_mn_try_ha_candidate(struct list_head *ha, struct in6_addr *addr) +{ + struct list_head *p; + + list_for_each(p, ha) { + struct dhaad_halist *e; + e = list_entry(p, typeof(*e), list); + if (ipv6_addr_cmp(addr, &e->addr) == 0) { + if (e->retry >= MAX_RETRIES_PER_HA) e->retry = -1; + else if (e->retry >= 0) e->retry++; + return 0; + } + } + return -1; +} + +/** + * mipv6_mn_get_bulifetime - Get lifetime for a binding update + * @home_addr: home address for BU + * @coa: care-of address for BU + * @flags: flags used for BU + * + * Returns maximum lifetime for BUs determined by the lifetime of + * care-of address and the lifetime of home address. + **/ +__u32 mipv6_mn_get_bulifetime(struct in6_addr *home_addr, struct in6_addr *coa, + __u8 flags) +{ + struct inet6_ifaddr *ifp_hoa, *ifp_coa; + __u32 lifetime = (flags & MIPV6_BU_F_HOME ? + HA_BU_DEF_LIFETIME : CN_BU_DEF_LIFETIME); + + ifp_hoa = ipv6_get_ifaddr(home_addr, NULL); + if(!ifp_hoa) { + DEBUG(DBG_INFO, "home address missing"); + return 0; + } + if (!(ifp_hoa->flags & IFA_F_PERMANENT)){ + if (ifp_hoa->valid_lft) + lifetime = min_t(__u32, lifetime, ifp_hoa->valid_lft); + else + DEBUG(DBG_ERROR, "Zero lifetime for home address"); + } + in6_ifa_put(ifp_hoa); + + ifp_coa = ipv6_get_ifaddr(coa, NULL); + if (!ifp_coa) { + DEBUG(DBG_INFO, "care-of address missing"); + return 0; + } + if (!(ifp_coa->flags & IFA_F_PERMANENT)) { + if(ifp_coa->valid_lft) + lifetime = min_t(__u32, lifetime, ifp_coa->valid_lft); + else + DEBUG(DBG_ERROR, + "Zero lifetime for care-of address"); + } + in6_ifa_put(ifp_coa); + + DEBUG(DBG_INFO, "Lifetime for binding is %ld", lifetime); + return lifetime; +} + +static int +mipv6_mn_tnl_rcv_send_bu_hook(struct ip6_tnl *t, struct sk_buff *skb) +{ + struct ipv6hdr *inner; + struct ipv6hdr *outer = skb->nh.ipv6h; + struct mn_info *minfo = NULL; + __u32 lifetime; + __u8 user_flags = 0; + + DEBUG_FUNC(); + + if (!is_mip6_tnl(t)) + return IP6_TNL_ACCEPT; + + if (!mip6node_cnf.accept_ret_rout) { + DEBUG(DBG_INFO, "Return routability administratively disabled" + " not doing route optimization"); + return IP6_TNL_ACCEPT; + } + if (!pskb_may_pull(skb, skb->h.raw-skb->data+sizeof(*inner))) + return IP6_TNL_DROP; + + inner = (struct ipv6hdr *)skb->h.raw; + + read_lock(&mn_info_lock); + minfo = mipv6_mninfo_get_by_home(&inner->daddr); + + if (!minfo) { + DEBUG(DBG_WARNING, "MN info missing"); + read_unlock(&mn_info_lock); + return IP6_TNL_ACCEPT; + } + DEBUG(DBG_DATADUMP, "MIPV6 MN: Received a tunneled IPv6 packet" + " to %x:%x:%x:%x:%x:%x:%x:%x," + " from %x:%x:%x:%x:%x:%x:%x:%x with\n tunnel header" + "daddr: %x:%x:%x:%x:%x:%x:%x:%x," + "saddr: %x:%x:%x:%x:%x:%x:%x:%x", + NIPV6ADDR(&inner->daddr), NIPV6ADDR(&inner->saddr), + NIPV6ADDR(&outer->daddr), NIPV6ADDR(&outer->saddr)); + + spin_lock(&minfo->lock); + + /* We don't send bus in response to all tunneled packets */ + + if (!ipv6_addr_cmp(&minfo->ha, &inner->saddr)) { + spin_unlock(&minfo->lock); + read_unlock(&mn_info_lock); + DEBUG(DBG_ERROR, "HA BUG: Received a tunneled packet " + "originally sent by home agent, not sending BU"); + return IP6_TNL_ACCEPT; + } + spin_unlock(&minfo->lock); + read_unlock(&mn_info_lock); + + DEBUG(DBG_DATADUMP, "Sending BU to correspondent node"); + + user_flags |= mip6node_cnf.bu_cn_ack ? MIPV6_BU_F_ACK : 0; + + if (inner->nexthdr != IPPROTO_DSTOPTS && + inner->nexthdr != IPPROTO_MOBILITY) { + struct in6_addr coa; + /* Don't start RR when receiving ICMP error messages */ + if (inner->nexthdr == IPPROTO_ICMPV6) { + int ptr = (u8*)(inner+1) - skb->data; + u8 type; + + if (skb_copy_bits(skb, + ptr+offsetof(struct icmp6hdr, + icmp6_type), + &type, 1) + || !(type & ICMPV6_INFOMSG_MASK)) { + return IP6_TNL_ACCEPT; + } + } + lifetime = mipv6_mn_get_bulifetime(&inner->daddr, + &outer->daddr, 0); + if (lifetime && + !mipv6_get_care_of_address(&inner->daddr, &coa)) { + write_lock(&bul_lock); + mipv6_send_bu(&inner->daddr, &inner->saddr, &coa, + INITIAL_BINDACK_TIMEOUT, + MAX_BINDACK_TIMEOUT, 1, + user_flags, + lifetime, NULL); + write_unlock(&bul_lock); + } + } + DEBUG(DBG_DATADUMP, "setting rcv_tunnel flag in skb"); + skb->security |= MIPV6_RCV_TUNNEL; + return IP6_TNL_ACCEPT; +} + +static struct ip6_tnl_hook_ops mipv6_mn_tnl_rcv_send_bu_ops = { + {NULL, NULL}, + IP6_TNL_PRE_DECAP, + IP6_TNL_PRI_FIRST, + mipv6_mn_tnl_rcv_send_bu_hook +}; + +static int +mipv6_mn_tnl_xmit_stats_hook(struct ip6_tnl *t, struct sk_buff *skb) +{ + DEBUG_FUNC(); + if (is_mip6_tnl(t)) + MIPV6_INC_STATS(n_encapsulations); + return IP6_TNL_ACCEPT; +} + +static struct ip6_tnl_hook_ops mipv6_mn_tnl_xmit_stats_ops = { + {NULL, NULL}, + IP6_TNL_PRE_ENCAP, + IP6_TNL_PRI_LAST, + mipv6_mn_tnl_xmit_stats_hook +}; + +static int +mipv6_mn_tnl_rcv_stats_hook(struct ip6_tnl *t, struct sk_buff *skb) +{ + DEBUG_FUNC(); + if (is_mip6_tnl(t)) + MIPV6_INC_STATS(n_decapsulations); + return IP6_TNL_ACCEPT; +} + +static struct ip6_tnl_hook_ops mipv6_mn_tnl_rcv_stats_ops = { + {NULL, NULL}, + IP6_TNL_PRE_DECAP, + IP6_TNL_PRI_LAST, + mipv6_mn_tnl_rcv_stats_hook +}; + +static void mn_check_tunneled_packet(struct sk_buff *skb) +{ + DEBUG_FUNC(); + /* If tunnel flag was set */ + if (skb->security & MIPV6_RCV_TUNNEL) { + struct in6_addr coa; + __u32 lifetime; + __u8 user_flags = 0; + int ptr = (u8*)(skb->nh.ipv6h+1) - skb->data; + int len = skb->len - ptr; + __u8 nexthdr = skb->nh.ipv6h->nexthdr; + + if (len < 0) + return; + + ptr = ipv6_skip_exthdr(skb, ptr, &nexthdr, len); + if (ptr < 0) + return; + + if (!mip6node_cnf.accept_ret_rout) { + DEBUG(DBG_INFO, "Return routability administratively disabled"); + return; + } + if (nexthdr == IPPROTO_MOBILITY) + return; + + /* Don't start RR when receiving ICMP error messages */ + if (nexthdr == IPPROTO_ICMPV6) { + u8 type; + + if (skb_copy_bits(skb, + ptr+offsetof(struct icmp6hdr, + icmp6_type), + &type, 1) + || !(type & ICMPV6_INFOMSG_MASK)) { + return; + } + } + user_flags |= mip6node_cnf.bu_cn_ack ? MIPV6_BU_F_ACK : 0; + mipv6_get_care_of_address(&skb->nh.ipv6h->daddr, &coa); + lifetime = mipv6_mn_get_bulifetime(&skb->nh.ipv6h->daddr, + &coa, 0); + + DEBUG(DBG_WARNING, "packet to address %x:%x:%x:%x:%x:%x:%x:%x" + "was tunneled. Sending BU to CN" + "%x:%x:%x:%x:%x:%x:%x:%x", + NIPV6ADDR(&skb->nh.ipv6h->daddr), + NIPV6ADDR(&skb->nh.ipv6h->saddr)); + /* This should work also with home address option */ + + write_lock(&bul_lock); + mipv6_send_bu(&skb->nh.ipv6h->daddr, &skb->nh.ipv6h->saddr, + &coa, INITIAL_BINDACK_TIMEOUT, + MAX_BINDACK_TIMEOUT, 1, user_flags, + lifetime, NULL); + write_unlock(&bul_lock); + } +} + +static int sched_mv_home_addr_task(struct in6_addr *haddr, int plen_new, + int newif, int oldif, struct handoff *ho) +{ + int alloc_size; + struct ifr_holder *ifrh; + + alloc_size = sizeof(*ifrh) + (ho ? sizeof(*ho): 0); + if ((ifrh = kmalloc(alloc_size, GFP_ATOMIC)) == NULL) { + DEBUG(DBG_ERROR, "Out of memory"); + return -1; + } + if (ho) { + ifrh->ho = (struct handoff *)((struct ifr_holder *)(ifrh + 1)); + memcpy(ifrh->ho, ho, sizeof(*ho)); + } else + ifrh->ho = NULL; + + /* must queue task to avoid deadlock with rtnl */ + ifrh->ifr.ifr6_ifindex = newif; + ifrh->ifr.ifr6_prefixlen = plen_new; + ipv6_addr_copy(&ifrh->ifr.ifr6_addr, haddr); + ifrh->old_ifi = oldif; + + spin_lock_bh(&ifrh_lock); + list_add_tail(&ifrh->list, &ifrh_list); + spin_unlock_bh(&ifrh_lock); + + schedule_task(&mv_home_addr_task); + + return 0; +} + +static void send_ret_home_ns(struct in6_addr *ha_addr, + struct in6_addr *home_addr, + int ifindex) +{ + struct in6_addr nil; + struct in6_addr mcaddr; + struct net_device *dev = dev_get_by_index(ifindex); + if (!dev) + return; + memset(&nil, 0, sizeof(nil)); + addrconf_addr_solict_mult(home_addr, &mcaddr); + ndisc_send_ns(dev, NULL, home_addr, &mcaddr, &nil); + dev_put(dev); +} + +static inline int ha_is_reachable(int ifindex, struct in6_addr *ha) +{ + struct net_device *dev; + int reachable = 0; + + dev = dev_get_by_index(ifindex); + if (dev) { + struct neighbour *neigh; + if ((neigh = ndisc_get_neigh(dev, ha)) != NULL) { + read_lock_bh(&neigh->lock); + if (neigh->nud_state&NUD_VALID) + reachable = 1; + read_unlock_bh(&neigh->lock); + neigh_release(neigh); + } + dev_put(dev); + } + return reachable; +} + +static int mn_ha_handoff(struct handoff *ho) +{ + struct list_head *lh; + struct mn_info *minfo; + struct in6_addr *coa= ho->coa; + int wait_mv_home = 0; + + read_lock_bh(&mn_info_lock); + list_for_each(lh, &mn_info_list) { + __u8 has_home_reg; + int ifindex; + struct in6_addr ha; + __u8 athome; + __u32 lifetime; + struct mipv6_bul_entry *entry = NULL; + + minfo = list_entry(lh, struct mn_info, list); + spin_lock(&minfo->lock); + has_home_reg = minfo->has_home_reg; + ifindex = minfo->ifindex; + ipv6_addr_copy(&ha, &minfo->ha); + + if (mipv6_prefix_compare(&ho->rtr_addr, &minfo->home_addr, + ho->plen)) { + if (minfo->has_home_reg) + athome = minfo->is_at_home = MN_RETURNING_HOME; + else + athome = minfo->is_at_home = MN_AT_HOME; + coa = &minfo->home_addr; + + spin_unlock(&minfo->lock); +#if 0 + /* Cancel prefix solicitation, rtr is our HA */ + mipv6_pfx_cancel_send(&ho->rtr_addr, ifindex); +#endif + minfo->ifindex = ho->ifindex; + + if (minfo->has_home_reg && + !ha_is_reachable(ho->ifindex, &minfo->ha)) { + send_ret_home_ns(&minfo->ha, + &minfo->home_addr, + ho->ifindex); + mipv6_mdet_set_curr_rtr_reachable(0); + wait_mv_home++; + } + if (ifindex != ho->ifindex){ + wait_mv_home++; + DEBUG(DBG_INFO, + "Moving home address back to " + "the home interface"); + sched_mv_home_addr_task(&minfo->home_addr, + 128, + ho->ifindex, + ifindex, ho); + } + if (!has_home_reg || wait_mv_home) + continue; + + lifetime = 0; + + } else { + athome = minfo->is_at_home = MN_NOT_AT_HOME; + if (minfo->ifindex_user != minfo->ifindex) { + DEBUG(DBG_INFO, "Scheduling home address move to virtual interface"); + sched_mv_home_addr_task(&minfo->home_addr, + 128, + minfo->ifindex_user, + minfo->ifindex, ho); /* Is minfo->ifindex correct */ + + wait_mv_home++; + } + minfo->ifindex = minfo->ifindex_user; + spin_unlock(&minfo->lock); + if (wait_mv_home) + continue; + if (!has_home_reg && + init_home_registration(&minfo->home_addr, + ho->coa)) { + continue; + } + lifetime = mipv6_mn_get_bulifetime(&minfo->home_addr, + ho->coa, + MIPV6_BU_F_HOME); + + } + write_lock(&bul_lock); + if (!(entry = mipv6_bul_get(&ha, &minfo->home_addr)) || + !(entry->flags & MIPV6_BU_F_HOME)) { + DEBUG(DBG_ERROR, + "Unable to find home registration for " + "home address: %x:%x:%x:%x:%x:%x:%x:%x!\n", + NIPV6ADDR(&minfo->home_addr)); + write_unlock(&bul_lock); + continue; + } + DEBUG(DBG_INFO, "Sending home de ? %d registration for " + "home address: %x:%x:%x:%x:%x:%x:%x:%x\n" + "to home agent %x:%x:%x:%x:%x:%x:%x:%x, " + "with lifetime %ld", + (athome != MN_NOT_AT_HOME), + NIPV6ADDR(&entry->home_addr), + NIPV6ADDR(&entry->cn_addr), lifetime); + mipv6_send_bu(&entry->home_addr, &entry->cn_addr, + coa, INITIAL_BINDACK_TIMEOUT, + MAX_BINDACK_TIMEOUT, 1, entry->flags, + lifetime, NULL); + write_unlock(&bul_lock); + + } + read_unlock_bh(&mn_info_lock); + return wait_mv_home; +} +/** + * mn_cn_handoff - called for every bul entry to send BU to CN + * @rawentry: bul entry + * @args: handoff event + * @sortkey: + * + * Since MN can have many home addresses and home networks, every BUL + * entry needs to be checked + **/ +int mn_cn_handoff(void *rawentry, void *args, unsigned long *sortkey) +{ + struct mipv6_bul_entry *entry = (struct mipv6_bul_entry *)rawentry; + struct in6_addr *coa = (struct in6_addr *)args; + + DEBUG_FUNC(); + + /* Home registrations already handled by mn_ha_handoff */ + if (entry->flags & MIPV6_BU_F_HOME) + return ITERATOR_CONT; + + /* BUL is locked by mipv6_mobile_node_moved which calls us + through mipv6_bul_iterate */ + + if (mipv6_prefix_compare(coa, + &entry->home_addr, + 64)) { + mipv6_send_bu(&entry->home_addr, &entry->cn_addr, + &entry->home_addr, INITIAL_BINDACK_TIMEOUT, + MAX_BINDACK_TIMEOUT, 1, entry->flags, 0, + NULL); + } else { + u32 lifetime = mipv6_mn_get_bulifetime(&entry->home_addr, + coa, + entry->flags); + mipv6_send_bu(&entry->home_addr, &entry->cn_addr, + coa, INITIAL_BINDACK_TIMEOUT, + MAX_BINDACK_TIMEOUT, 1, entry->flags, + lifetime, NULL); + } + return ITERATOR_CONT; +} + + +int mn_bul_invalidate(void *rawentry, void *args, unsigned long *sortkey) +{ + struct mipv6_bul_entry *bul = (struct mipv6_bul_entry *)rawentry; + struct bul_inval_args *arg = (struct bul_inval_args *)args; + + DEBUG_FUNC(); + + if (!ipv6_addr_cmp(arg->cn, &bul->cn_addr) && + (!ipv6_addr_cmp(arg->mn, &bul->home_addr) || + !ipv6_addr_cmp(arg->mn, &bul->coa))) { + if (arg->all_rr_states || !bul->rr || + (bul->rr->rr_state != RR_INIT && + bul->rr->rr_state != RR_DONE)) { + bul->state = ACK_ERROR; + bul->callback = bul_entry_expired; + bul->callback_time = jiffies + + DUMB_CN_BU_LIFETIME * HZ; + bul->expire = bul->callback_time; + DEBUG(DBG_INFO, "BUL entry set to ACK_ERROR"); + mipv6_bul_reschedule(bul); + } + } + return ITERATOR_CONT; +} +/** + * init_home_registration - start Home Registration process + * @home_addr: home address + * @coa: care-of address + * + * Checks whether we have a Home Agent address for this home address. + * If not starts Dynamic Home Agent Address Discovery. Otherwise + * tries to register with home agent if not already registered. + * Returns 1, if home registration process is started and 0 otherwise + **/ +int init_home_registration(struct in6_addr *home_addr, struct in6_addr *coa) +{ + struct mn_info *hinfo; + struct in6_addr ha; + __u8 man_conf; + int ifindex; + __u32 lifetime; + __u8 user_flags = 0, flags; + + DEBUG_FUNC(); + + read_lock_bh(&mn_info_lock); + if ((hinfo = mipv6_mninfo_get_by_home(home_addr)) == NULL) { + DEBUG(DBG_ERROR, "No mn_info found for address: " + "%x:%x:%x:%x:%x:%x:%x:%x", + NIPV6ADDR(home_addr)); + read_unlock_bh(&mn_info_lock); + return -ENOENT; + } + spin_lock(&hinfo->lock); + if (mipv6_prefix_compare(&hinfo->home_addr, coa, hinfo->home_plen)) { + spin_unlock(&hinfo->lock); + read_unlock_bh(&mn_info_lock); + DEBUG(DBG_INFO, "Adding home address, MN at home"); + return 1; + } + if (ipv6_addr_any(&hinfo->ha)) { + int dhaad_id = mipv6_get_dhaad_id(); + hinfo->dhaad_id = dhaad_id; + spin_unlock(&hinfo->lock); + mipv6_icmpv6_send_dhaad_req(home_addr, hinfo->home_plen, dhaad_id); + read_unlock_bh(&mn_info_lock); + DEBUG(DBG_INFO, + "Home Agent address not set, initiating DHAAD"); + return 1; + } + ipv6_addr_copy(&ha, &hinfo->ha); + man_conf = hinfo->man_conf; + ifindex = hinfo->ifindex; + spin_unlock(&hinfo->lock); + read_unlock_bh(&mn_info_lock); +#if 0 + if (man_conf) + mipv6_pfx_add_ha(&ha, coa, ifindex); +#endif + if (mipv6_bul_exists(&ha, home_addr)) { + DEBUG(DBG_INFO, "BU already sent to HA"); + return 0; + } + /* user flags received through sysctl */ + user_flags |= mip6node_cnf.bu_lladdr ? MIPV6_BU_F_LLADDR : 0; + user_flags |= mip6node_cnf.bu_keymgm ? MIPV6_BU_F_KEYMGM : 0; + + flags = MIPV6_BU_F_HOME | MIPV6_BU_F_ACK | user_flags; + + lifetime = mipv6_mn_get_bulifetime(home_addr, coa, flags); + + DEBUG(DBG_INFO, "Sending initial home registration for " + "home address: %x:%x:%x:%x:%x:%x:%x:%x\n" + "to home agent %x:%x:%x:%x:%x:%x:%x:%x, " + "with lifetime %ld, prefixlength %d", + NIPV6ADDR(home_addr), NIPV6ADDR(&ha), lifetime, 0); + + write_lock_bh(&bul_lock); + mipv6_send_bu(home_addr, &ha, coa, INITIAL_BINDACK_DAD_TIMEOUT, + MAX_BINDACK_TIMEOUT, 1, flags, lifetime, NULL); + write_unlock_bh(&bul_lock); + + return 1; +} + +/** + * mipv6_mobile_node_moved - Send BUs to all HAs and CNs + * @ho: handoff structure contains the new and previous routers + * + * Event for handoff. Sends BUs everyone on Binding Update List. + **/ +int mipv6_mobile_node_moved(struct handoff *ho) +{ +#if 0 + int bu_to_prev_router = 1; +#endif + int dummy; + + DEBUG_FUNC(); + + ma_ctl_upd_iface(ho->ifindex, + MA_IFACE_CURRENT | MA_IFACE_HAS_ROUTER, &dummy); + + /* First send BU to HA, then to all other nodes that are on BU list */ + if (mn_ha_handoff(ho) != 0) + return 0; /* Wait for move home address task */ +#if 0 + /* Add current care-of address to mn_info list, if current router acts + as a HA.*/ + + if (ho->home_address && bu_to_prev_router) + mipv6_mninfo_add(ho->coa, ho->plen, + MN_AT_HOME, 0, &ho->rtr_addr, + ho->plen, ROUTER_BU_DEF_LIFETIME, + 0); + +#endif + return 0; +} + +/** + * mipv6_mn_send_home_na - send NA when returning home + * @haddr: home address to advertise + * + * After returning home, MN must advertise all its valid addresses in + * home link to all nodes. + **/ +void mipv6_mn_send_home_na(struct in6_addr *haddr) +{ + struct net_device *dev = NULL; + struct in6_addr mc_allnodes; + struct mn_info *hinfo = NULL; + + read_lock(&mn_info_lock); + hinfo = mipv6_mninfo_get_by_home(haddr); + if (!hinfo) { + read_unlock(&mn_info_lock); + return; + } + spin_lock(&hinfo->lock); + hinfo->is_at_home = MN_AT_HOME; + dev = dev_get_by_index(hinfo->ifindex); + spin_unlock(&hinfo->lock); + read_unlock(&mn_info_lock); + if (dev == NULL) { + DEBUG(DBG_ERROR, "Send home_na: device not found."); + return; + } + + ipv6_addr_all_nodes(&mc_allnodes); + ndisc_send_na(dev, NULL, &mc_allnodes, haddr, 0, 0, 1, 1); + dev_put(dev); +} + +static int mn_use_hao(struct in6_addr *daddr, struct in6_addr *saddr) +{ + struct mipv6_bul_entry *entry; + struct mn_info *minfo = NULL; + int add_ha = 0; + + read_lock_bh(&mn_info_lock); + minfo = mipv6_mninfo_get_by_home(saddr); + if (minfo && minfo->is_at_home != MN_AT_HOME) { + read_lock_bh(&bul_lock); + if ((entry = mipv6_bul_get(daddr, saddr)) == NULL) { + read_unlock_bh(&bul_lock); + read_unlock_bh(&mn_info_lock); + return add_ha; + } + add_ha = (entry->state != ACK_ERROR && + (!entry->rr || entry->rr->rr_state == RR_DONE || + entry->flags & MIPV6_BU_F_HOME)); + read_unlock_bh(&bul_lock); + } + read_unlock_bh(&mn_info_lock); + return add_ha; +} + +static int +mn_dev_event(struct notifier_block *nb, unsigned long event, void *ptr) +{ + struct net_device *dev = ptr; + struct list_head *lh; + struct mn_info *minfo; + int newif = 0; + + /* here are probably the events we need to worry about */ + switch (event) { + case NETDEV_UP: + DEBUG(DBG_DATADUMP, "New netdevice %s registered.", dev->name); + if (dev->type != ARPHRD_LOOPBACK && !dev_is_mip6_tnl(dev)) + ma_ctl_add_iface(dev->ifindex); + + break; + case NETDEV_GOING_DOWN: + DEBUG(DBG_DATADUMP, "Netdevice %s disappeared.", dev->name); + /* + * Go through mn_info list and move all home addresses on the + * netdev going down to a new device. This will make it + * practically impossible for the home address to return home, + * but allow MN to retain its connections using the address. + */ + + read_lock_bh(&mn_info_lock); + list_for_each(lh, &mn_info_list) { + minfo = list_entry(lh, struct mn_info, list); + spin_lock(&minfo->lock); + if (minfo->ifindex == dev->ifindex) { + if (sched_mv_home_addr_task(&minfo->home_addr, 128, + minfo->ifindex_user, + 0, NULL) < 0) { + minfo->ifindex = 0; + spin_unlock(&minfo->lock); + read_unlock_bh(&mn_info_lock); + return NOTIFY_DONE; + } else { + minfo->ifindex = minfo->ifindex_user; + if (minfo->is_at_home) { + minfo->is_at_home = 0; + + } + newif = minfo->ifindex_user; + } + } + spin_unlock(&minfo->lock); + } + + read_unlock_bh(&mn_info_lock); + } + ma_ctl_upd_iface(dev->ifindex, MA_IFACE_NOT_PRESENT, &newif); + mipv6_mdet_del_if(dev->ifindex); + + return NOTIFY_DONE; +} + +struct notifier_block mipv6_mn_dev_notifier = { + mn_dev_event, + NULL, + 0 /* check if using zero is ok */ +}; + +static void deprecate_addr(struct mn_info *minfo) +{ + /* + * Lookup address from IPv6 address list and set deprecated flag + */ + +} + +/* + * Required because we can only modify addresses after the packet is + * constructed. We otherwise mess with higher level protocol + * pseudoheaders. With strict protocol layering life would be SO much + * easier! + */ +static unsigned int modify_xmit_addrs(unsigned int hooknum, + struct sk_buff **pskb, + const struct net_device *in, + const struct net_device *out, + int (*okfn) (struct sk_buff *)) +{ + struct sk_buff *skb = *pskb; + + DEBUG_FUNC(); + + if (skb) { + struct ipv6hdr *hdr = skb->nh.ipv6h; + struct inet6_skb_parm *opt = (struct inet6_skb_parm *)skb->cb; + struct mipv6_bul_entry *bule; + struct in6_addr *daddr; + + if (!ipv6_addr_any(&opt->hoa)) + daddr = &opt->hoa; + else + daddr = &hdr->daddr; + + /* We don't consult bul when sending a BU to avoid deadlock, since + * BUL is already locked. + */ + + + if (opt->mipv6_flags & MIPV6_SND_HAO && + !(opt->mipv6_flags & MIPV6_SND_BU)) { + write_lock(&bul_lock); + bule = mipv6_bul_get(daddr, &hdr->saddr); + if (!bule) { + write_unlock(&bul_lock); + return NF_ACCEPT; + } + if (!bule->rr || bule->rr->rr_state == RR_DONE || + bule->flags & MIPV6_BU_F_HOME) { + DEBUG(DBG_DATADUMP, + "Replace source address with CoA and reroute"); + ipv6_addr_copy(&hdr->saddr, &bule->coa); + skb->nfcache |= NFC_ALTERED; + } + write_unlock(&bul_lock); + } else if (opt->mipv6_flags & MIPV6_SND_HAO) { + mipv6_get_care_of_address(&hdr->saddr, &hdr->saddr); + skb->nfcache |= NFC_ALTERED; + } + } + return NF_ACCEPT; +} + +/* We set a netfilter hook so that we can modify outgoing packet's + * source addresses + */ +struct nf_hook_ops addr_modify_hook_ops = { + {NULL, NULL}, /* List head, no predecessor, no successor */ + modify_xmit_addrs, + PF_INET6, + NF_IP6_LOCAL_OUT, + NF_IP6_PRI_FIRST /* Should be of EXTREMELY high priority since we + * do not want to mess with IPSec (possibly + * implemented as packet filter) + */ +}; + +#define MN_INFO_LEN 77 + +static int mn_proc_info(char *buffer, char **start, off_t offset, + int length) +{ + struct list_head *p; + struct mn_info *minfo; + int len = 0, skip = 0; + + DEBUG_FUNC(); + + read_lock_bh(&mn_info_lock); + list_for_each(p, &mn_info_list) { + if (len < offset / MN_INFO_LEN) { + skip++; + continue; + } + if (len >= length) + break; + minfo = list_entry(p, struct mn_info, list); + spin_lock(&minfo->lock); + len += sprintf(buffer + len, "%02d %08x%08x%08x%08x %02x " + "%08x%08x%08x%08x %d %d\n", + minfo->ifindex, + ntohl(minfo->home_addr.s6_addr32[0]), + ntohl(minfo->home_addr.s6_addr32[1]), + ntohl(minfo->home_addr.s6_addr32[2]), + ntohl(minfo->home_addr.s6_addr32[3]), + minfo->home_plen, + ntohl(minfo->ha.s6_addr32[0]), + ntohl(minfo->ha.s6_addr32[1]), + ntohl(minfo->ha.s6_addr32[2]), + ntohl(minfo->ha.s6_addr32[3]), + minfo->is_at_home, minfo->has_home_reg); + spin_unlock(&minfo->lock); + } + read_unlock_bh(&mn_info_lock); + + *start = buffer; + if (offset) + *start += offset % MN_INFO_LEN; + + len -= offset % MN_INFO_LEN; + + if (len > length) + len = length; + if (len < 0) + len = 0; + + return len; +} + +int mipv6_mn_ha_nd_update(struct net_device *dev, + struct in6_addr *ha, u8 *lladdr) +{ + int valid = 0; + struct neighbour *neigh; + if ((neigh = ndisc_get_neigh(dev, ha))) { + read_lock(&neigh->lock); + valid = neigh->nud_state & NUD_VALID; + read_unlock(&neigh->lock); + if (!valid && lladdr) + neigh_update(neigh, lladdr, NUD_REACHABLE, 0, 1); + neigh_release(neigh); + } + return valid; +} + +int mipv6_mn_ha_probe(struct inet6_ifaddr *ifp, u8 *lladdr) +{ + struct mn_info *minfo; + + if (!(minfo = mipv6_mninfo_get_by_home(&ifp->addr)) || + ipv6_addr_any(&minfo->ha)) + return 0; + + if (mipv6_mn_ha_nd_update(ifp->idev->dev, &minfo->ha, lladdr)) + mipv6_mdet_retrigger_ho(); + return 1; +} + +int __init mipv6_mn_init(void) +{ + struct net_device *dev; + + DEBUG_FUNC(); + + if (mipv6_add_tnl_to_ha()) + return -ENODEV; + + mipv6_bul_init(MIPV6_BUL_SIZE); + mip6_fn.mn_use_hao = mn_use_hao; + mip6_fn.mn_check_tunneled_packet = mn_check_tunneled_packet; + INIT_TQUEUE(&mv_home_addr_task, mv_home_addr, NULL); + + ma_ctl_init(); + for (dev = dev_base; dev; dev = dev->next) { + if (dev->flags & IFF_UP && + dev->type != ARPHRD_LOOPBACK && !dev_is_mip6_tnl(dev)) { + ma_ctl_add_iface(dev->ifindex); + } + } + DEBUG(DBG_INFO, "Multiaccess support initialized"); + + register_netdevice_notifier(&mipv6_mn_dev_notifier); + register_inet6addr_notifier(&mipv6_mn_inet6addr_notifier); + + ip6ip6_tnl_register_hook(&mipv6_mn_tnl_rcv_send_bu_ops); + ip6ip6_tnl_register_hook(&mipv6_mn_tnl_xmit_stats_ops); + ip6ip6_tnl_register_hook(&mipv6_mn_tnl_rcv_stats_ops); + + MIPV6_SETCALL(mipv6_set_home, mipv6_mn_set_home); + + mipv6_initialize_mdetect(); + + /* COA to home transformation hook */ + MIPV6_SETCALL(mipv6_get_home_address, mipv6_get_saddr_hook); + MIPV6_SETCALL(mipv6_mn_ha_probe, mipv6_mn_ha_probe); + MIPV6_SETCALL(mipv6_is_home_addr, mipv6_mn_is_home_addr); + proc_net_create("mip6_mninfo", 0, mn_proc_info); + /* Set packet modification hook (source addresses) */ + nf_register_hook(&addr_modify_hook_ops); + + return 0; +} + +void __exit mipv6_mn_exit(void) +{ + struct list_head *lh, *tmp; + struct mn_info *minfo; + DEBUG_FUNC(); + + mip6_fn.mn_use_hao = NULL; + mip6_fn.mn_check_tunneled_packet = NULL; + + MIPV6_RESETCALL(mipv6_set_home); + MIPV6_RESETCALL(mipv6_get_home_address); + MIPV6_RESETCALL(mipv6_mn_ha_probe); + MIPV6_RESETCALL(mipv6_is_home_addr); + nf_unregister_hook(&addr_modify_hook_ops); + proc_net_remove("mip6_mninfo"); + mipv6_shutdown_mdetect(); + ip6ip6_tnl_unregister_hook(&mipv6_mn_tnl_rcv_stats_ops); + ip6ip6_tnl_unregister_hook(&mipv6_mn_tnl_xmit_stats_ops); + ip6ip6_tnl_unregister_hook(&mipv6_mn_tnl_rcv_send_bu_ops); + ma_ctl_clean(); + + unregister_inet6addr_notifier(&mipv6_mn_inet6addr_notifier); + unregister_netdevice_notifier(&mipv6_mn_dev_notifier); + write_lock_bh(&mn_info_lock); + + list_for_each_safe(lh, tmp, &mn_info_list) { + minfo = list_entry(lh, struct mn_info, list); + if (minfo->is_at_home == MN_NOT_AT_HOME) + deprecate_addr(minfo); + list_del(&minfo->list); + kfree(minfo); + } + write_unlock_bh(&mn_info_lock); + mipv6_bul_exit(); + flush_scheduled_tasks(); + mipv6_del_tnl_to_ha(); +} diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/mn.h linux-2.4.25/net/ipv6/mobile_ip6/mn.h --- linux-2.4.25.old/net/ipv6/mobile_ip6/mn.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.25/net/ipv6/mobile_ip6/mn.h 2004-06-26 11:29:31.000000000 +0100 @@ -0,0 +1,96 @@ +/* + * MIPL Mobile IPv6 Mobile Node header file + * + * $Id$ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _MN_H +#define _MN_H + +#include + +/* constants for sending of BUs*/ +#define HA_BU_DEF_LIFETIME 10000 +#define CN_BU_DEF_LIFETIME 420 /* Max lifetime for RR bindings from RFC 3775 */ +#define DUMB_CN_BU_LIFETIME 600 /* BUL entry lifetime in case of dumb CN */ +#define ROUTER_BU_DEF_LIFETIME 30 /* For packet forwarding from previous coa */ +#define ERROR_DEF_LIFETIME DUMB_CN_BU_LIFETIME + +extern rwlock_t mn_info_lock; + +#define MN_NOT_AT_HOME 0 +#define MN_RETURNING_HOME 1 +#define MN_AT_HOME 2 + +/* + * Mobile Node information record + */ +struct mn_info { + struct in6_addr home_addr; + struct in6_addr ha; + __u8 home_plen; + __u8 is_at_home; + __u8 has_home_reg; + __u8 man_conf; + int ifindex; + int ifindex_user; + unsigned long home_addr_expires; + unsigned short dhaad_id; + struct list_head list; + spinlock_t lock; +}; + +/* prototypes for interface functions */ +int mipv6_mn_init(void); +void mipv6_mn_exit(void); + +struct handoff; + +/* Interface to movement detection */ +int mipv6_mobile_node_moved(struct handoff *ho); + +void mipv6_mn_send_home_na(struct in6_addr *haddr); +/* Init home reg. with coa */ +int init_home_registration(struct in6_addr *home_addr, struct in6_addr *coa); + +/* mn_info functions that require locking by caller */ +struct mn_info *mipv6_mninfo_get_by_home(struct in6_addr *haddr); + +struct mn_info *mipv6_mninfo_get_by_ha(struct in6_addr *home_agent); + +struct mn_info *mipv6_mninfo_get_by_id(unsigned short id); + +/* "safe" mn_info functions */ +void mipv6_mninfo_add(int ifindex, struct in6_addr *home_addr, int plen, + int isathome, unsigned long lifetime, struct in6_addr *ha, + int ha_plen, unsigned long ha_lifetime, int man_conf); + +int mipv6_mninfo_del(struct in6_addr *home_addr, int del_dyn_only); + +void mipv6_mn_set_home_reg(struct in6_addr *home_addr, int has_home_reg); + +int mipv6_mn_is_at_home(struct in6_addr *addr); + +int mipv6_mn_is_home_addr(struct in6_addr *addr); + +__u32 mipv6_mn_get_bulifetime(struct in6_addr *home_addr, + struct in6_addr *coa, __u8 flags); +int mn_cn_handoff(void *rawentry, void *args, unsigned long *sortkey); + +int mipv6_mn_ha_nd_update(struct net_device *dev, + struct in6_addr *ha, u8 *lladdr); + +struct bul_inval_args { + int all_rr_states; + struct in6_addr *cn; + struct in6_addr *mn; +}; + +int mn_bul_invalidate(void *rawentry, void *args, unsigned long *sortkey); + +#endif /* _MN_H */ diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/mobhdr.h linux-2.4.25/net/ipv6/mobile_ip6/mobhdr.h --- linux-2.4.25.old/net/ipv6/mobile_ip6/mobhdr.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.25/net/ipv6/mobile_ip6/mobhdr.h 2004-06-26 11:29:31.000000000 +0100 @@ -0,0 +1,101 @@ +/* + * MIPL Mobile IPv6 Mobility Header send and receive + * + * $Id$ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _MOBHDR_H +#define _MOBHDR_H + +#include + +/* RR states for mipv6_send_bu() */ +#define RR_INIT 0x00 +#define RR_WAITH 0x01 +#define RR_WAITC 0x02 +#define RR_WAITHC 0x13 +#define RR_DONE 0x10 + +#define MH_UNKNOWN_CN 1 +#define MH_AUTH_FAILED 2 +#define MH_SEQUENCE_MISMATCH 3 + +struct mipv6_bul_entry; +struct sk_buff; + +int mipv6_mh_common_init(void); +void mipv6_mh_common_exit(void); +int mipv6_mh_mn_init(void); +void mipv6_mh_mn_exit(void); + +struct mipv6_mh_opt { + struct mipv6_mo_alt_coa *alt_coa; + struct mipv6_mo_nonce_indices *nonce_indices; + struct mipv6_mo_bauth_data *auth_data; + struct mipv6_mo_br_advice *br_advice; + int freelen; + int totlen; + u8 *next_free; + u8 data[0]; +}; + +struct mobopt { + struct mipv6_mo_alt_coa *alt_coa; + struct mipv6_mo_nonce_indices *nonce_indices; + struct mipv6_mo_bauth_data *auth_data; + struct mipv6_mo_br_advice *br_advice; +}; + +struct mipv6_mh_opt *alloc_mh_opts(int totlen); +int append_mh_opt(struct mipv6_mh_opt *ops, u8 type, u8 len, void *data); +int parse_mo_tlv(void *mos, int len, struct mobopt *opts); +int mipv6_add_pad(u8 *data, int n); + +struct mipv6_auth_parm { + struct in6_addr *coa; + struct in6_addr *cn_addr; + __u8 *k_bu; +}; + +int send_mh(struct in6_addr *daddr, struct in6_addr *saddr, + u8 msg_type, u8 msg_len, u8 *msg, + struct in6_addr *hao_addr, struct in6_addr *rth_addr, + struct mipv6_mh_opt *ops, struct mipv6_auth_parm *parm); + +int mipv6_mh_register(int type, int (*func)(struct sk_buff *, + struct in6_addr *, struct in6_addr *, + struct in6_addr *, struct in6_addr *, struct mipv6_mh *)); + +void mipv6_mh_unregister(int type); + +int mipv6_send_brr(struct in6_addr *saddr, struct in6_addr *daddr, + struct mipv6_mh_opt *ops); + +int mipv6_send_bu(struct in6_addr *saddr, struct in6_addr *daddr, + struct in6_addr *coa, __u32 initdelay, + __u32 maxackdelay, __u8 exp, __u8 flags, + __u32 lifetime, struct mipv6_mh_opt *ops); + +int mipv6_send_be(struct in6_addr *saddr, struct in6_addr *daddr, + struct in6_addr *home, __u8 status); + +int mipv6_send_ba(struct in6_addr *saddr, struct in6_addr *daddr, + struct in6_addr *auth_coa, struct in6_addr *rep_coa, + u8 status, u16 sequence, u32 lifetime, u8 *k_bu); + +/* Binding Authentication Data Option routines */ +#define MAX_HASH_LENGTH 20 +#define MIPV6_RR_MAC_LENGTH 12 + +int mipv6_auth_build(struct in6_addr *cn_addr, struct in6_addr *coa, + __u8 *opt, __u8 *aud_data, __u8 *k_bu); + +int mipv6_auth_check(struct in6_addr *cn_addr, struct in6_addr *coa, + __u8 *opt, __u8 optlen, struct mipv6_mo_bauth_data *aud, + __u8 *k_bu); +#endif /* _MOBHDR_H */ diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/mobhdr_common.c linux-2.4.25/net/ipv6/mobile_ip6/mobhdr_common.c --- linux-2.4.25.old/net/ipv6/mobile_ip6/mobhdr_common.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.25/net/ipv6/mobile_ip6/mobhdr_common.c 2004-06-26 11:29:31.000000000 +0100 @@ -0,0 +1,1210 @@ +/* + * Mobile IPv6 Mobility Header Common Functions + * + * Authors: + * Antti Tuominen + * + * $Id: s.mh_recv.c 1.159 02/10/16 15:01:29+03:00 antti@traci.mipl.mediapoli.com $ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "stats.h" +#include "debug.h" +#include "mobhdr.h" +#include "bcache.h" + +#include "rr_crypto.h" +#include "exthdrs.h" +#include "config.h" + +#define MIPV6_MH_MAX MIPV6_MH_BE +struct mh_proto { + int (*func) (struct sk_buff *, + struct in6_addr *, struct in6_addr *, + struct in6_addr *, struct in6_addr *, + struct mipv6_mh *); +}; + +static struct mh_proto mh_rcv[MIPV6_MH_MAX]; + +int mipv6_mh_register(int type, int (*func)(struct sk_buff *, + struct in6_addr *, struct in6_addr *, + struct in6_addr *, struct in6_addr *, struct mipv6_mh *)) +{ + if (mh_rcv[type].func != NULL) + return -1; + + mh_rcv[type].func = func; + + return 0; +} + +void mipv6_mh_unregister(int type) +{ + if (type < 0 || type > MIPV6_MH_MAX) + return; + + mh_rcv[type].func = NULL; +} + +struct socket *mipv6_mh_socket = NULL; + +/* TODO: Fix fragmentation */ +static int dstopts_getfrag( + const void *data, struct in6_addr *addr, + char *buff, unsigned int offset, unsigned int len) +{ + memcpy(buff, data + offset, len); + return 0; +} + +struct mipv6_mh_opt *alloc_mh_opts(int totlen) +{ + struct mipv6_mh_opt *ops; + + ops = kmalloc(sizeof(*ops) + totlen, GFP_ATOMIC); + if (ops == NULL) + return NULL; + + memset(ops, 0, sizeof(*ops)); + ops->next_free = ops->data; + ops->freelen = totlen; + + return ops; +} + +int append_mh_opt(struct mipv6_mh_opt *ops, u8 type, u8 len, void *data) +{ + struct mipv6_mo *mo; + + if (ops->next_free == NULL) { + DEBUG(DBG_ERROR, "No free room for option"); + return -ENOMEM; + } + if (ops->freelen < len + 2) { + DEBUG(DBG_ERROR, "No free room for option"); + return -ENOMEM; + } + else { + ops->freelen -= (len + 2); + ops->totlen += (len + 2); + } + + mo = (struct mipv6_mo *)ops->next_free; + mo->type = type; + mo->length = len; + + switch (type) { + case MIPV6_OPT_ALTERNATE_COA: + ops->alt_coa = (struct mipv6_mo_alt_coa *)mo; + ipv6_addr_copy(&ops->alt_coa->addr, (struct in6_addr *)data); + break; + case MIPV6_OPT_NONCE_INDICES: + DEBUG(DBG_INFO, "Added nonce indices pointer"); + ops->nonce_indices = (struct mipv6_mo_nonce_indices *)mo; + ops->nonce_indices->home_nonce_i = *(__u16 *)data; + ops->nonce_indices->careof_nonce_i = *((__u16 *)data + 1); + break; + case MIPV6_OPT_AUTH_DATA: + DEBUG(DBG_INFO, "Added opt auth_data pointer"); + ops->auth_data = (struct mipv6_mo_bauth_data *)mo; + break; + case MIPV6_OPT_BIND_REFRESH_ADVICE: + ops->br_advice = (struct mipv6_mo_br_advice *)mo; + ops->br_advice->refresh_interval = htons(*(u16 *)data); + break; + default: + DEBUG(DBG_ERROR, "Unknow option type"); + break; + } + + if (ops->freelen == 0) + ops->next_free = NULL; + else + ops->next_free += (len + 2); + + return 0; +} + +/* + * Calculates required padding with xn + y requirement with offset + */ +static inline int optpad(int xn, int y, int offset) +{ + return ((y - offset) & (xn - 1)); +} + +static int option_pad(int type, int offset) +{ + if (type == MIPV6_OPT_ALTERNATE_COA) + return optpad(8, 6, offset); /* 8n + 6 */ + if (type == MIPV6_OPT_BIND_REFRESH_ADVICE || + type == MIPV6_OPT_NONCE_INDICES) + return optpad(2, 0, offset); /* 2n */ + return 0; +} + +/* + * Add Pad1 or PadN option to data + */ +int mipv6_add_pad(u8 *data, int n) +{ + struct mipv6_mo_padn *padn; + + if (n <= 0) return 0; + if (n == 1) { + *data = MIPV6_OPT_PAD1; + return 1; + } + padn = (struct mipv6_mo_padn *)data; + padn->type = MIPV6_OPT_PADN; + padn->length = n - 2; + memset(padn->data, 0, n - 2); + return n; +} + +/* + * Write options to mobility header buffer + */ +static int prepare_mh_opts(u8 *optdata, int off, struct mipv6_mh_opt *ops) +{ + u8 *nextopt = optdata; + int offset = off, pad = 0; + + if (ops == NULL) { + nextopt = NULL; + return -1; + } + + if (ops->alt_coa) { + pad = option_pad(MIPV6_OPT_ALTERNATE_COA, offset); + nextopt += mipv6_add_pad(nextopt, pad); + memcpy(nextopt, ops->alt_coa, sizeof(struct mipv6_mo_alt_coa)); + nextopt += sizeof(struct mipv6_mo_alt_coa); + offset += pad + sizeof(struct mipv6_mo_alt_coa); + } + + if (ops->br_advice) { + pad = option_pad(MIPV6_OPT_BIND_REFRESH_ADVICE, offset); + nextopt += mipv6_add_pad(nextopt, pad); + memcpy(nextopt, ops->br_advice, sizeof(struct mipv6_mo_br_advice)); + nextopt += sizeof(struct mipv6_mo_br_advice); + offset += pad + sizeof(struct mipv6_mo_br_advice); + } + + if (ops->nonce_indices) { + pad = option_pad(MIPV6_OPT_NONCE_INDICES, offset); + nextopt += mipv6_add_pad(nextopt, pad); + memcpy(nextopt, ops->nonce_indices, sizeof(struct mipv6_mo_nonce_indices)); + nextopt += sizeof(struct mipv6_mo_nonce_indices); + offset += pad + sizeof(struct mipv6_mo_nonce_indices); + } + + if (ops->auth_data) { + /* This option should always be the last. Header + * length must be a multiple of 8 octects, so we pad + * if necessary. */ + pad = optpad(8, 0, offset + ops->auth_data->length + 2); + nextopt += mipv6_add_pad(nextopt, pad); + memcpy(nextopt, ops->auth_data, ops->auth_data->length + 2); + nextopt += ops->auth_data->length + 2; + } + nextopt = NULL; + + return 0; +} + +static int calculate_mh_opts(struct mipv6_mh_opt *ops, int mh_len) +{ + int offset = mh_len; + + if (ops == NULL) + return 0; + + if (ops->alt_coa) + offset += sizeof(struct mipv6_mo_alt_coa) + + option_pad(MIPV6_OPT_ALTERNATE_COA, offset); + + if (ops->br_advice) + offset += sizeof(struct mipv6_mo_br_advice) + + option_pad(MIPV6_OPT_BIND_REFRESH_ADVICE, offset); + + if (ops->nonce_indices) + offset += sizeof(struct mipv6_mo_nonce_indices) + + option_pad(MIPV6_OPT_NONCE_INDICES, offset); + + if (ops->auth_data) /* no alignment */ + offset += ops->auth_data->length + 2; + + return offset - mh_len; +} + +/* + * + * Mobility Header Message send functions + * + */ + +/** + * send_mh - builds and sends a MH msg + * + * @daddr: destination address for packet + * @saddr: source address for packet + * @msg_type: type of MH + * @msg_len: message length + * @msg: MH type specific data + * @hao_addr: home address for home address option + * @rth_addr: routing header address + * @ops: mobility options + * @parm: auth data + * + * Builds MH, appends the type specific msg data to the header and + * sends the packet with a home address option, if a home address was + * given. Returns 0, if everything succeeded and a negative error code + * otherwise. + **/ +int send_mh(struct in6_addr *daddr, + struct in6_addr *saddr, + u8 msg_type, u8 msg_len, u8 *msg, + struct in6_addr *hao_addr, + struct in6_addr *rth_addr, + struct mipv6_mh_opt *ops, + struct mipv6_auth_parm *parm) +{ + struct flowi fl; + struct mipv6_mh *mh; + struct sock *sk = mipv6_mh_socket->sk; + struct ipv6_txoptions *txopt = NULL; + int tot_len = sizeof(struct mipv6_mh) + msg_len; + int padded_len = 0, txopt_len = 0; + + DEBUG_FUNC(); + /* Add length of options */ + tot_len += calculate_mh_opts(ops, tot_len); + /* Needs to be a multiple of 8 octets */ + padded_len = tot_len + optpad(8, 0, tot_len); + + mh = sock_kmalloc(sk, padded_len, GFP_ATOMIC); + if (!mh) { + DEBUG(DBG_ERROR, "memory allocation failed"); + return -ENOMEM; + } + + memset(&fl, 0, sizeof(fl)); + fl.proto = IPPROTO_MOBILITY; + fl.fl6_dst = daddr; + fl.fl6_src = saddr; + fl.fl6_flowlabel = 0; + fl.oif = sk->bound_dev_if; + + if (hao_addr || rth_addr) { + __u8 *opt_ptr; + + if (hao_addr) + txopt_len += sizeof(struct mipv6_dstopt_homeaddr) + 6; + if (rth_addr) + txopt_len += sizeof(struct rt2_hdr); + + txopt_len += sizeof(*txopt); + txopt = sock_kmalloc(sk, txopt_len, GFP_ATOMIC); + if (txopt == NULL) { + DEBUG(DBG_ERROR, "No socket space left"); + sock_kfree_s(sk, mh, padded_len); + return -ENOMEM; + } + memset(txopt, 0, txopt_len); + txopt->tot_len = txopt_len; + opt_ptr = (__u8 *) (txopt + 1); + if (hao_addr) { + int holen = sizeof(struct mipv6_dstopt_homeaddr) + 6; + txopt->dst1opt = (struct ipv6_opt_hdr *) opt_ptr; + txopt->opt_flen += holen; + opt_ptr += holen; + mipv6_append_dst1opts(txopt->dst1opt, saddr, + NULL, holen); + txopt->mipv6_flags = MIPV6_SND_HAO | MIPV6_SND_BU; + } + if (rth_addr) { + int rtlen = sizeof(struct rt2_hdr); + txopt->srcrt2 = (struct ipv6_rt_hdr *) opt_ptr; + txopt->opt_nflen += rtlen; + opt_ptr += rtlen; + mipv6_append_rt2hdr(txopt->srcrt2, rth_addr); + } + } + + /* Fill in the fields of MH */ + mh->payload = NEXTHDR_NONE; + mh->length = (padded_len >> 3) - 1; /* Units of 8 octets - 1 */ + mh->type = msg_type; + mh->reserved = 0; + mh->checksum = 0; + + memcpy(mh->data, msg, msg_len); + prepare_mh_opts(mh->data + msg_len, msg_len + sizeof(*mh), ops); + /* If BAD is present, this is already done. */ + mipv6_add_pad((u8 *)mh + tot_len, padded_len - tot_len); + + if (parm && parm->k_bu && ops && ops->auth_data) { + /* Calculate the position of the authorization data before adding checksum*/ + mipv6_auth_build(parm->cn_addr, parm->coa, (__u8 *)mh, + (__u8 *)mh + padded_len - MIPV6_RR_MAC_LENGTH, parm->k_bu); + } + /* Calculate the MH checksum */ + mh->checksum = csum_ipv6_magic(fl.fl6_src, fl.fl6_dst, + padded_len, IPPROTO_MOBILITY, + csum_partial((char *)mh, padded_len, 0)); + ip6_build_xmit(sk, dstopts_getfrag, mh, &fl, padded_len, txopt, 255, + MSG_DONTWAIT); + /* dst cache must be cleared so RR messages can be routed through + different interfaces */ + sk_dst_reset(sk); + + if (txopt_len) + sock_kfree_s(sk, txopt, txopt_len); + sock_kfree_s(sk, mh, padded_len); + return 0; +} + +/** + * mipv6_send_brr - send a Binding Refresh Request + * @saddr: source address for BRR + * @daddr: destination address for BRR + * @ops: mobility options + * + * Sends a binding request. On a mobile node, use the mobile node's + * home address for @saddr. Returns 0 on success, negative on + * failure. + **/ +int mipv6_send_brr(struct in6_addr *saddr, struct in6_addr *daddr, + struct mipv6_mh_opt *ops) +{ + struct mipv6_mh_brr br; + + memset(&br, 0, sizeof(br)); + /* We don't need to explicitly add a RH to brr, since it will be + * included automatically, if a BCE exists + */ + MIPV6_INC_STATS(n_brr_sent); + return send_mh(daddr, saddr, MIPV6_MH_BRR, sizeof(br), (u8 *)&br, + NULL, NULL, ops, NULL); +} + +/** + * mipv6_send_ba - send a Binding Acknowledgement + * @saddr: source address for BA + * @daddr: destination address for BA + * @reply_coa: destination care-of address of MN + * @auth_coa: care-of address of MN used for authentication + * @status: status field value + * @sequence: sequence number from BU + * @lifetime: granted lifetime for binding in seconds + * @ops: mobility options + * + * Send a binding acknowledgement. On a mobile node, use the mobile + * node's home address for saddr. Returns 0 on success, non-zero on + * failure. + **/ +int mipv6_send_ba(struct in6_addr *saddr, struct in6_addr *daddr, + struct in6_addr *auth_coa, struct in6_addr *rep_coa, + u8 status, u16 sequence, u32 lifetime, u8 *k_bu) +{ + struct mipv6_mh_ba ba; + struct mipv6_auth_parm parm; + struct mipv6_mh_opt *ops = NULL; + int ops_len = 0, ret = 0; + struct mipv6_bce bc_entry; + int coming_home = 0; + int bypass_tnl = 0; + + memset(&ba, 0, sizeof(ba)); + + ba.status = status; + ba.sequence = htons(sequence); + ba.lifetime = htons(lifetime >> 2); + + DEBUG(DBG_INFO, "sending a status %d BA %s authenticator to MN \n" + "%x:%x:%x:%x:%x:%x:%x:%x at care of address \n" + "%x:%x:%x:%x:%x:%x:%x:%x : with lifetime %d and \n" + " sequence number %d", + status, k_bu ? "with" : "without", + NIPV6ADDR(daddr), NIPV6ADDR(auth_coa), lifetime, sequence); + + memset(&parm, 0, sizeof(parm)); + parm.coa = auth_coa; + parm.cn_addr = saddr; + + if (k_bu) { + ops_len += sizeof(struct mipv6_mo_bauth_data) + + MIPV6_RR_MAC_LENGTH; + parm.k_bu = k_bu; + } + + if (mip6node_cnf.binding_refresh_advice) { + ops_len += sizeof(struct mipv6_mo_br_advice); + } + if (ops_len) { + ops = alloc_mh_opts(ops_len); + if (ops == NULL) { + DEBUG(DBG_WARNING, "Out of memory"); + return -ENOMEM; + } + if (mip6node_cnf.binding_refresh_advice > 0) { + if (append_mh_opt(ops, MIPV6_OPT_BIND_REFRESH_ADVICE, 2, + &mip6node_cnf.binding_refresh_advice) < 0) { + DEBUG(DBG_WARNING, "Adding BRA failed"); + if (ops) + kfree(ops); + return -ENOMEM; + } + } + if (k_bu) { + if (append_mh_opt(ops, MIPV6_OPT_AUTH_DATA, + MIPV6_RR_MAC_LENGTH, NULL) < 0) { + DEBUG(DBG_WARNING, "Adding BAD failed"); + if (ops) + kfree(ops); + return -ENOMEM; + } + } + } + coming_home = !ipv6_addr_cmp(rep_coa, daddr); + + bypass_tnl = (coming_home && + !mipv6_bcache_get(daddr, saddr, &bc_entry) && + bc_entry.flags&MIPV6_BU_F_HOME && + status >= 128); + + if (bypass_tnl && mip6_fn.bce_tnl_rt_del) + mip6_fn.bce_tnl_rt_del(&bc_entry.coa, + &bc_entry.our_addr, + &bc_entry.home_addr); + + if (coming_home) + ret = send_mh(daddr, saddr, MIPV6_MH_BA, sizeof(ba), (u8 *)&ba, + NULL, NULL, ops, &parm); + else + ret = send_mh(daddr, saddr, MIPV6_MH_BA, sizeof(ba), (u8 *)&ba, + NULL, rep_coa, ops, &parm); + + if (bypass_tnl && mip6_fn.bce_tnl_rt_add) + mip6_fn.bce_tnl_rt_add(&bc_entry.coa, + &bc_entry.our_addr, + &bc_entry.home_addr); + + if (ret == 0) { + if (status < 128) { + MIPV6_INC_STATS(n_ba_sent); + } else { + MIPV6_INC_STATS(n_ban_sent); + } + } + + if (ops) + kfree(ops); + + return 0; +} + +/** + * mipv6_send_be - send a Binding Error message + * @saddr: source address for BE + * @daddr: destination address for BE + * @home: Home Address in offending packet (if any) + * + * Sends a binding error. On a mobile node, use the mobile node's + * home address for @saddr. Returns 0 on success, negative on + * failure. + **/ +int mipv6_send_be(struct in6_addr *saddr, struct in6_addr *daddr, + struct in6_addr *home, __u8 status) +{ + struct mipv6_mh_be be; + int ret = 0; + struct mipv6_bce bc_entry; + int bypass_tnl = 0; + + if (ipv6_addr_is_multicast(daddr)) + return -EINVAL; + + memset(&be, 0, sizeof(be)); + be.status = status; + if (home) + ipv6_addr_copy(&be.home_addr, home); + + if (mipv6_bcache_get(daddr, saddr, &bc_entry) == 0 && + bc_entry.flags&MIPV6_BU_F_HOME) + bypass_tnl = 1; + + if (bypass_tnl && mip6_fn.bce_tnl_rt_del) + mip6_fn.bce_tnl_rt_del(&bc_entry.coa, + &bc_entry.our_addr, + &bc_entry.home_addr); + + ret = send_mh(daddr, saddr, MIPV6_MH_BE, sizeof(be), (u8 *)&be, + NULL, NULL, NULL, NULL); + + if (bypass_tnl && mip6_fn.bce_tnl_rt_add) + mip6_fn.bce_tnl_rt_add(&bc_entry.coa, + &bc_entry.our_addr, + &bc_entry.home_addr); + + if (ret == 0) + MIPV6_INC_STATS(n_be_sent); + + return ret; +} + +/** + * mipv6_send_addr_test - send a HoT or CoT message + * @saddr: source address + * @daddr: destination address + * @msg_type: HoT or CoT message + * @init: HoTI or CoTI message + * + * Send a reply to HoTI or CoTI message. + **/ +static int mipv6_send_addr_test(struct in6_addr *saddr, + struct in6_addr *daddr, + int msg_type, + struct mipv6_mh_addr_ti *init) +{ + u_int8_t *kgen_token = NULL; + struct mipv6_mh_addr_test addr_test; + struct mipv6_rr_nonce *nonce; + struct mipv6_mh_opt *ops = NULL; + int ret = 0; + + DEBUG_FUNC(); + + if ((nonce = mipv6_rr_get_new_nonce())== NULL) { + DEBUG(DBG_WARNING, "Nonce creation failed"); + return 0; + } + if (mipv6_rr_cookie_create(daddr, &kgen_token, nonce->index)) { + DEBUG(DBG_WARNING, "No cookie"); + return 0; + } + + addr_test.nonce_index = nonce->index; + memcpy(addr_test.init_cookie, init->init_cookie, + MIPV6_RR_COOKIE_LENGTH); + memcpy(addr_test.kgen_token, kgen_token, + MIPV6_RR_COOKIE_LENGTH); + + /* No options defined */ + ret = send_mh(daddr, saddr, msg_type, sizeof(addr_test), + (u8 *)&addr_test, NULL, NULL, ops, NULL); + + if (ret == 0) { + if (msg_type == MIPV6_MH_HOT) { + MIPV6_INC_STATS(n_hot_sent); + } else { + MIPV6_INC_STATS(n_cot_sent); + } + } + + return 0; +} + +static void bc_cache_add(int ifindex, struct in6_addr *daddr, + struct in6_addr *haddr, struct in6_addr *coa, + struct in6_addr *rep_coa, __u32 lifetime, + __u16 sequence, __u8 flags, __u8 *k_bu) +{ + __u8 ba_status = SUCCESS; + + if (lifetime > MAX_RR_BINDING_LIFE) + lifetime = MAX_RR_BINDING_LIFE; + + if (mipv6_bcache_add(ifindex, daddr, haddr, coa, lifetime, + sequence, flags, CACHE_ENTRY) != 0) { + DEBUG(DBG_ERROR, "binding failed."); + ba_status = INSUFFICIENT_RESOURCES; + } + + if (flags & MIPV6_BU_F_ACK) { + DEBUG(DBG_INFO, "sending ack (code=%d)", ba_status); + mipv6_send_ba(daddr, haddr, coa, rep_coa, ba_status, sequence, + lifetime, k_bu); + } +} + +static void bc_cn_home_add(int ifindex, struct in6_addr *daddr, + struct in6_addr *haddr, struct in6_addr *coa, + struct in6_addr *rep_coa, __u32 lifetime, + __u16 sequence, __u8 flags, __u8 *k_bu) +{ + mipv6_send_ba(daddr, haddr, coa, rep_coa, + HOME_REGISTRATION_NOT_SUPPORTED, + sequence, lifetime, k_bu); +} + +static void bc_cache_delete(struct in6_addr *daddr, struct in6_addr *haddr, + struct in6_addr *coa, struct in6_addr *rep_coa, + __u16 sequence, __u8 flags, + __u8 *k_bu) +{ + __u8 status = SUCCESS; + + /* Cached Care-of Address Deregistration */ + if (mipv6_bcache_exists(haddr, daddr) == CACHE_ENTRY) { + mipv6_bcache_delete(haddr, daddr, CACHE_ENTRY); + } else { + DEBUG(DBG_INFO, "entry is not in cache"); + status = REASON_UNSPECIFIED; + } + if (flags & MIPV6_BU_F_ACK) { + mipv6_send_ba(daddr, haddr, coa, rep_coa, status, sequence, + 0, k_bu); + } +} + +static void bc_cn_home_delete(struct in6_addr *daddr, struct in6_addr *haddr, + struct in6_addr *coa, struct in6_addr *rep_coa, + __u16 sequence, __u8 flags, + __u8 *k_bu) +{ +} + +/** + * parse_mo_tlv - Parse TLV-encoded Mobility Options + * @mos: pointer to Mobility Options + * @len: total length of options + * @opts: structure to store option pointers + * + * Parses Mobility Options passed in @mos. Stores pointers in @opts + * to all valid mobility options found in @mos. Unknown options and + * padding (%MIPV6_OPT_PAD1 and %MIPV6_OPT_PADN) is ignored and + * skipped. + **/ +int parse_mo_tlv(void *mos, int len, struct mobopt *opts) +{ + struct mipv6_mo *curr = (struct mipv6_mo *)mos; + int left = len; + + while (left > 0) { + int optlen = 0; + if (curr->type == MIPV6_OPT_PAD1) + optlen = 1; + else + optlen = 2 + curr->length; + + if (optlen > left) + goto bad; + + switch (curr->type) { + case MIPV6_OPT_PAD1: + DEBUG(DBG_DATADUMP, "MIPV6_OPT_PAD1 at %x", curr); + break; + case MIPV6_OPT_PADN: + DEBUG(DBG_DATADUMP, "MIPV6_OPT_PADN at %x", curr); + break; + case MIPV6_OPT_ALTERNATE_COA: + DEBUG(DBG_DATADUMP, "MIPV6_OPT_ACOA at %x", curr); + opts->alt_coa = (struct mipv6_mo_alt_coa *)curr; + break; + case MIPV6_OPT_NONCE_INDICES: + DEBUG(DBG_DATADUMP, "MIPV6_OPT_NONCE_INDICES at %x", curr); + opts->nonce_indices = + (struct mipv6_mo_nonce_indices *)curr; + break; + case MIPV6_OPT_AUTH_DATA: + DEBUG(DBG_DATADUMP, "MIPV6_OPT_AUTH_DATA at %x", curr); + opts->auth_data = (struct mipv6_mo_bauth_data *)curr; + break; + case MIPV6_OPT_BIND_REFRESH_ADVICE: + DEBUG(DBG_DATADUMP, "MIPV6_OPT_BIND_REFRESH_ADVICE at %x", curr); + opts->br_advice = (struct mipv6_mo_br_advice *)curr; + break; + default: + DEBUG(DBG_INFO, "MO Unknown option type %d at %x, ignoring.", + curr->type, curr); + /* unknown mobility option, ignore and skip */ + } + + (u8 *)curr += optlen; + left -= optlen; + } + + if (left == 0) + return 0; + bad: + return -1; +} + +/* + * + * Mobility Header Message handlers + * + */ + +static int mipv6_handle_mh_testinit(struct sk_buff *skb, + struct in6_addr *cn, + struct in6_addr *lcoa, + struct in6_addr *saddr, + struct in6_addr *fcoa, + struct mipv6_mh *mh) +{ + struct mipv6_mh_addr_ti *ti = (struct mipv6_mh_addr_ti *)mh->data; + int msg_len = (mh->length+1) << 3; + int opt_len; + DEBUG_FUNC(); + + if (msg_len > skb->len) + return -1; + + opt_len = msg_len - sizeof(*mh) - sizeof(*ti); + + if (opt_len < 0) { + __u32 pos = (__u32)&mh->length - (__u32)skb->nh.raw; + icmpv6_send(skb, ICMPV6_PARAMPROB, + ICMPV6_HDR_FIELD, pos, skb->dev); + + DEBUG(DBG_INFO, "Mobility Header length less than H/C TestInit"); + return -1; + } + if (!mip6node_cnf.accept_ret_rout) { + DEBUG(DBG_INFO, "Return routability administratively disabled"); + return -1; + } + if (lcoa || fcoa) { + DEBUG(DBG_INFO, "H/C TestInit has HAO or RTH2, dropped."); + return -1; + } + + if (mh->type == MIPV6_MH_HOTI) { + MIPV6_INC_STATS(n_hoti_rcvd); + return mipv6_send_addr_test(cn, saddr, MIPV6_MH_HOT, ti); + } else if (mh->type == MIPV6_MH_COTI) { + MIPV6_INC_STATS(n_coti_rcvd); + return mipv6_send_addr_test(cn, saddr, MIPV6_MH_COT, ti); + } else + return -1; /* Impossible to get here */ +} + +/** + * mipv6_handle_mh_bu - Binding Update handler + * @src: care-of address of sender + * @dst: our address + * @haddr: home address of sender + * @mh: pointer to the beginning of the Mobility Header + * + * Handles Binding Update. Packet and offset to option are passed. + * Returns 0 on success, otherwise negative. + **/ +static int mipv6_handle_mh_bu(struct sk_buff *skb, + struct in6_addr *dst, + struct in6_addr *unused, + struct in6_addr *haddr, + struct in6_addr *coaddr, + struct mipv6_mh *mh) +{ + struct mipv6_mh_bu *bu = (struct mipv6_mh_bu *)mh->data; + int msg_len = (mh->length+1) << 3; + int opt_len; + int auth = 0; + int dereg; /* Is this deregistration? */ + int addr_type; + + struct mipv6_bce bc_entry; + struct in6_addr *coa, *reply_coa; + __u8 *key_bu = NULL; /* RR BU authentication key */ + __u8 flags = bu->flags; + __u16 sequence; + __u32 lifetime; + __u16 nonce_ind = (__u16) -1; + + if (msg_len > skb->len) + return -1; + + opt_len = msg_len - sizeof(*mh) - sizeof(*bu); + + if (opt_len < 0) { + __u32 pos = (__u32)&mh->length - (__u32)skb->nh.raw; + icmpv6_send(skb, ICMPV6_PARAMPROB, + ICMPV6_HDR_FIELD, pos, skb->dev); + + DEBUG(DBG_INFO, "Mobility Header length less than BU"); + MIPV6_INC_STATS(n_bu_drop.invalid); + return -1; + } + + addr_type = ipv6_addr_type(haddr); + if (addr_type&IPV6_ADDR_LINKLOCAL || !(addr_type&IPV6_ADDR_UNICAST)) + return -EINVAL; + + /* If HAO not present, CoA == HAddr */ + if (coaddr == NULL) + coa = haddr; + else { + coa = coaddr; + addr_type = ipv6_addr_type(coa); + if (addr_type&IPV6_ADDR_LINKLOCAL || + !(addr_type&IPV6_ADDR_UNICAST)) + return -EINVAL; + } + reply_coa = coa; + + sequence = ntohs(bu->sequence); + if (bu->lifetime == 0xffff) + lifetime = 0xffffffff; + else + lifetime = ntohs(bu->lifetime) << 2; + + dereg = (ipv6_addr_cmp(haddr, coa) == 0 || lifetime == 0); + + if (opt_len > 0) { + struct mobopt opts; + memset(&opts, 0, sizeof(opts)); + if (parse_mo_tlv(bu + 1, opt_len, &opts) < 0) { + MIPV6_INC_STATS(n_bu_drop.invalid); + return -1; + } + /* + * MIPV6_OPT_AUTH_DATA, MIPV6_OPT_NONCE_INDICES, + * MIPV6_OPT_ALT_COA + */ + if (opts.alt_coa) { + coa = &opts.alt_coa->addr; + dereg = (ipv6_addr_cmp(haddr, coa) == 0 || lifetime == 0); + } + addr_type = ipv6_addr_type(coa); + if (addr_type&IPV6_ADDR_LINKLOCAL || + !(addr_type&IPV6_ADDR_UNICAST)) + return -EINVAL; + + if (flags & MIPV6_BU_F_HOME) { + if (opts.nonce_indices) + return -1; + } else { + u8 ba_status = 0; + u8 *h_ckie = NULL, *c_ckie = NULL; /* Home and care-of cookies */ + + /* BUs to CN MUST include authorization data and nonce indices options */ + if (!opts.auth_data || !opts.nonce_indices) { + DEBUG(DBG_WARNING, + "Route optimization BU without authorization material, aborting processing"); + return MH_AUTH_FAILED; + } + if (mipv6_rr_cookie_create( + haddr, &h_ckie, opts.nonce_indices->home_nonce_i) < 0) { + DEBUG(DBG_WARNING, + "mipv6_rr_cookie_create failed for home cookie"); + ba_status = EXPIRED_HOME_NONCE_INDEX; + } + nonce_ind = opts.nonce_indices->home_nonce_i; + /* Don't create the care-of cookie, if MN deregisters */ + if (!dereg && mipv6_rr_cookie_create( + coa, &c_ckie, + opts.nonce_indices->careof_nonce_i) < 0) { + DEBUG(DBG_WARNING, + "mipv6_rr_cookie_create failed for coa cookie"); + if (ba_status == 0) + ba_status = EXPIRED_CAREOF_NONCE_INDEX; + else + ba_status = EXPIRED_NONCES; + } + if (ba_status == 0) { + if (dereg) + key_bu = mipv6_rr_key_calc(h_ckie, NULL); + else + key_bu = mipv6_rr_key_calc(h_ckie, c_ckie); + mh->checksum = 0;/* TODO: Don't mangle the packet */ + if (key_bu && mipv6_auth_check( + dst, coa, (__u8 *)mh, msg_len + sizeof(*mh), opts.auth_data, key_bu) == 0) { + DEBUG(DBG_INFO, "mipv6_auth_check OK for BU"); + auth = 1; + } else { + DEBUG(DBG_WARNING, + "BU Authentication failed"); + } + } + if (h_ckie) + kfree(h_ckie); + if (c_ckie) + kfree(c_ckie); + if (ba_status != 0) { + MIPV6_INC_STATS(n_bu_drop.auth); + mipv6_send_ba(dst, haddr, coa, + reply_coa, ba_status, + sequence, 0, NULL); + goto out; + } + } + + } + /* Require authorization option for RO, home reg is protected by IPsec */ + if (!(flags & MIPV6_BU_F_HOME) && !auth) { + MIPV6_INC_STATS(n_bu_drop.auth); + if (key_bu) + kfree(key_bu); + return MH_AUTH_FAILED; + } + + if (mipv6_bcache_get(haddr, dst, &bc_entry) == 0) { + if ((bc_entry.flags&MIPV6_BU_F_HOME) != + (flags&MIPV6_BU_F_HOME)) { + DEBUG(DBG_INFO, + "Registration type change. Sending BA REG_TYPE_CHANGE_FORBIDDEN"); + mipv6_send_ba(dst, haddr, coa, reply_coa, + REG_TYPE_CHANGE_FORBIDDEN, + sequence, lifetime, key_bu); + goto out; + } + if (!MIPV6_SEQ_GT(sequence, bc_entry.seq)) { + DEBUG(DBG_INFO, + "Sequence number mismatch. Sending BA SEQUENCE_NUMBER_OUT_OF_WINDOW"); + mipv6_send_ba(dst, haddr, coa, reply_coa, + SEQUENCE_NUMBER_OUT_OF_WINDOW, + bc_entry.seq, lifetime, key_bu); + goto out; + } + } + + if (!dereg) { + int ifindex; + struct rt6_info *rt; + + /* Avoid looping binding cache entries */ + if (mipv6_bcache_get(coa, dst, &bc_entry) == 0) { + DEBUG(DBG_WARNING, "Looped BU, dropping the packet"); + goto out; + } + DEBUG(DBG_INFO, "calling bu_add."); + if ((rt = rt6_lookup(haddr, dst, 0, 0)) != NULL) { + ifindex = rt->rt6i_dev->ifindex; + dst_release(&rt->u.dst); + } else { + /* + * Can't process the BU since the right interface is + * not found. + */ + DEBUG(DBG_WARNING, "No route entry found for handling " + "a BU request, (using 0 as index)"); + ifindex = 0; + } + if (flags & MIPV6_BU_F_HOME) + mip6_fn.bce_home_add(ifindex, dst, haddr, coa, + reply_coa, lifetime, sequence, + flags, key_bu); + else + mip6_fn.bce_cache_add(ifindex, dst, haddr, coa, + reply_coa, lifetime, sequence, + flags, key_bu); + } else { + DEBUG(DBG_INFO, "calling BCE delete."); + + if (flags & MIPV6_BU_F_HOME) + mip6_fn.bce_home_del(dst, haddr, coa, reply_coa, + sequence, flags, key_bu); + else { + mipv6_rr_invalidate_nonce(nonce_ind); + mip6_fn.bce_cache_del(dst, haddr, coa, reply_coa, + sequence, flags, key_bu); + } + } + out: + MIPV6_INC_STATS(n_bu_rcvd); + if (key_bu) + kfree(key_bu); + return 0; +} + +static int mipv6_mh_rcv(struct sk_buff *skb) +{ + struct inet6_skb_parm *opt = (struct inet6_skb_parm *)skb->cb; + struct mipv6_mh *mh; + struct in6_addr *lhome, *fhome, *lcoa = NULL, *fcoa = NULL; + int ret = 0; + + fhome = &skb->nh.ipv6h->saddr; + lhome = &skb->nh.ipv6h->daddr; + + if (opt->hao != 0) { + struct mipv6_dstopt_homeaddr *hao; + hao = (struct mipv6_dstopt_homeaddr *)(skb->nh.raw + opt->hao); + fcoa = &hao->addr; + } + + if (opt->srcrt2 != 0) { + struct rt2_hdr *rt2; + rt2 = (struct rt2_hdr *)((u8 *)skb->nh.raw + opt->srcrt2); + lcoa = &rt2->addr; + } + + /* Verify checksum is correct */ + if (skb->ip_summed == CHECKSUM_HW) { + skb->ip_summed = CHECKSUM_UNNECESSARY; + if (csum_ipv6_magic(fhome, lhome, skb->len, IPPROTO_MOBILITY, + skb->csum)) { + if (net_ratelimit()) + printk(KERN_WARNING "MIPv6 MH hw checksum failed\n"); + skb->ip_summed = CHECKSUM_NONE; + } + } + if (skb->ip_summed == CHECKSUM_NONE) { + if (csum_ipv6_magic(fhome, lhome, skb->len, IPPROTO_MOBILITY, + skb_checksum(skb, 0, skb->len, 0))) { + if (net_ratelimit()) + printk(KERN_WARNING "MIPv6 MH checksum failed\n"); + goto bad; + } + } + + if (!pskb_may_pull(skb, skb->h.raw-skb->data+sizeof(*mh)) || + !pskb_may_pull(skb, + skb->h.raw-skb->data+((skb->h.raw[1]+1)<<3))) { + DEBUG(DBG_INFO, "MIPv6 MH invalid length"); + kfree_skb(skb); + return 0; + } + + mh = (struct mipv6_mh *) skb->h.raw; + + /* Verify there are no more headers after the MH */ + if (mh->payload != NEXTHDR_NONE) { + __u32 pos = (__u32)&mh->payload - (__u32)skb->nh.raw; + icmpv6_send(skb, ICMPV6_PARAMPROB, + ICMPV6_HDR_FIELD, pos, skb->dev); + + DEBUG(DBG_INFO, "MIPv6 MH error"); + goto bad; + } + + if (mh->type > MIPV6_MH_MAX) { + /* send binding error */ + printk("Invalid mobility header type (%d)\n", mh->type); + mipv6_send_be(lhome, fcoa ? fcoa : fhome, + fcoa ? fhome : NULL, + MIPV6_BE_UNKNOWN_MH_TYPE); + goto bad; + } + if (mh_rcv[mh->type].func != NULL) { + ret = mh_rcv[mh->type].func(skb, lhome, lcoa, fhome, fcoa, mh); + } else { + DEBUG(DBG_INFO, "No handler for MH Type %d", mh->type); + goto bad; + } + + kfree_skb(skb); + return 0; + +bad: + MIPV6_INC_STATS(n_mh_in_error); + kfree_skb(skb); + return 0; + +} + +#if LINUX_VERSION_CODE >= 0x2052a +struct inet6_protocol mipv6_mh_protocol = +{ + mipv6_mh_rcv, /* handler */ + NULL /* error control */ +}; +#else +struct inet6_protocol mipv6_mh_protocol = +{ + mipv6_mh_rcv, /* handler */ + NULL, /* error control */ + NULL, /* next */ + IPPROTO_MOBILITY, /* protocol ID */ + 0, /* copy */ + NULL, /* data */ + "MIPv6 MH" /* name */ +}; +#endif + +/* + * + * Code module init/exit functions + * + */ + +int __init mipv6_mh_common_init(void) +{ + struct sock *sk; + int err; + + mip6_fn.bce_home_add = bc_cn_home_add; + mip6_fn.bce_cache_add = bc_cache_add; + mip6_fn.bce_home_del = bc_cn_home_delete; + mip6_fn.bce_cache_del = bc_cache_delete; + + mipv6_mh_socket = sock_alloc(); + if (mipv6_mh_socket == NULL) { + printk(KERN_ERR + "Failed to create the MIP6 MH control socket.\n"); + return -1; + } + mipv6_mh_socket->type = SOCK_RAW; + + if ((err = sock_create(PF_INET6, SOCK_RAW, IPPROTO_MOBILITY, + &mipv6_mh_socket)) < 0) { + printk(KERN_ERR + "Failed to initialize the MIP6 MH control socket (err %d).\n", + err); + sock_release(mipv6_mh_socket); + mipv6_mh_socket = NULL; /* for safety */ + return err; + } + + sk = mipv6_mh_socket->sk; + sk->allocation = GFP_ATOMIC; + sk->sndbuf = 64 * 1024 + sizeof(struct sk_buff); + sk->prot->unhash(sk); + + memset(&mh_rcv, 0, sizeof(mh_rcv)); + mh_rcv[MIPV6_MH_HOTI].func = mipv6_handle_mh_testinit; + mh_rcv[MIPV6_MH_COTI].func = mipv6_handle_mh_testinit; + mh_rcv[MIPV6_MH_BU].func = mipv6_handle_mh_bu; + +#if LINUX_VERSION_CODE >= 0x2052a + if (inet6_add_protocol(&mipv6_mh_protocol, IPPROTO_MOBILITY) < 0) { + printk(KERN_ERR "Failed to register MOBILITY protocol\n"); + sock_release(mipv6_mh_socket); + mipv6_mh_socket = NULL; + return -EAGAIN; + } +#else + inet6_add_protocol(&mipv6_mh_protocol); +#endif + /* To disable the use of dst_cache, + * which slows down the sending of BUs ?? + */ + sk->dst_cache=NULL; + + return 0; +} + +void __exit mipv6_mh_common_exit(void) +{ + if (mipv6_mh_socket) sock_release(mipv6_mh_socket); + mipv6_mh_socket = NULL; /* For safety. */ + +#if LINUX_VERSION_CODE >= 0x2052a + inet6_del_protocol(&mipv6_mh_protocol, IPPROTO_MOBILITY); +#else + inet6_del_protocol(&mipv6_mh_protocol); +#endif + memset(&mh_rcv, 0, sizeof(mh_rcv)); +} diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/mobhdr_mn.c linux-2.4.25/net/ipv6/mobile_ip6/mobhdr_mn.c --- linux-2.4.25.old/net/ipv6/mobile_ip6/mobhdr_mn.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.25/net/ipv6/mobile_ip6/mobhdr_mn.c 2004-06-26 11:29:32.000000000 +0100 @@ -0,0 +1,1155 @@ +/* + * Mobile IPv6 Mobility Header Functions for Mobile Node + * + * Authors: + * Antti Tuominen + * Niklas Kämpe + * Henrik Petander + * + * $Id:$ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include + +#include "mobhdr.h" +#include "mn.h" +#include "bul.h" +#include "rr_crypto.h" +#include "debug.h" +#include "util.h" +#include "stats.h" + +int rr_configured = 1; + +/* Return value of mipv6_rr_state() */ +#define NO_RR 0 +#define DO_RR 1 +#define RR_FOR_COA 2 +#define INPROGRESS_RR 3 + +/** + * send_bu_msg - sends a Binding Update + * @bulentry : BUL entry with the information for building a BU + * + * Function builds a BU msg based on the contents of a bul entry. + * Does not change the bul entry. + **/ +static int send_bu_msg(struct mipv6_bul_entry *binding) +{ + int auth = 0; /* Use auth */ + int ret = 0; + struct mipv6_auth_parm parm; + struct mipv6_mh_bu bu; + + if (!binding) { + DEBUG(DBG_ERROR, "called with a null bul entry"); + return -1; + } + + memset(&parm, 0, sizeof(parm)); + if (mipv6_prefix_compare(&binding->coa, &binding->home_addr, 64)) + parm.coa = &binding->home_addr; + else + parm.coa = &binding->coa; + parm.cn_addr = &binding->cn_addr; + + if (binding->rr && binding->rr->kbu) { + DEBUG(DBG_INFO, "Binding with key"); + auth = 1; + parm.k_bu = binding->rr->kbu; + } + memset(&bu, 0, sizeof(bu)); + bu.flags = binding->flags; + bu.sequence = htons(binding->seq); + bu.lifetime = htons(binding->lifetime >> 2); + bu.reserved = 0; + + ret = send_mh(&binding->cn_addr, &binding->home_addr, + MIPV6_MH_BU, sizeof(bu), (u8 *)&bu, + &binding->home_addr, NULL, + binding->ops, &parm); + + if (ret == 0) + MIPV6_INC_STATS(n_bu_sent); + + return ret; +} + +/** + * mipv6_send_addr_test_init - send a HoTI or CoTI message + * @saddr: source address for H/CoTI + * @daddr: destination address for H/CoTI + * @msg_type: Identifies whether HoTI or CoTI + * @init_cookie: the HoTi or CoTi init cookie + * + * The message will be retransmitted till we get a HoT or CoT message, since + * our caller (mipv6_RR_start) has entered this message in the BUL with + * exponential backoff retramission set. + */ +static int mipv6_send_addr_test_init(struct in6_addr *saddr, + struct in6_addr *daddr, + u8 msg_type, + u8 *init_cookie) +{ + struct mipv6_mh_addr_ti ti; + struct mipv6_mh_opt *ops = NULL; + int ret = 0; + + /* Set reserved and copy the cookie from address test init msg */ + ti.reserved = 0; + mipv6_rr_mn_cookie_create(init_cookie); + memcpy(ti.init_cookie, init_cookie, MIPV6_RR_COOKIE_LENGTH); + + ret = send_mh(daddr, saddr, msg_type, sizeof(ti), (u8 *)&ti, + NULL, NULL, ops, NULL); + if (ret == 0) { + if (msg_type == MIPV6_MH_HOTI) { + MIPV6_INC_STATS(n_hoti_sent); + } else { + MIPV6_INC_STATS(n_coti_sent); + } + } + + return ret; +} + +/* + * + * Callback handlers for binding update list + * + */ + +/* Return value 0 means keep entry, non-zero means discard entry. */ + +/* Callback for BUs not requiring acknowledgement + */ +int bul_entry_expired(struct mipv6_bul_entry *bulentry) +{ + /* Lifetime expired, delete entry. */ + DEBUG(DBG_INFO, "bul entry 0x%p lifetime expired, deleting entry", + bulentry); + return 1; +} + +/* Callback for BUs requiring acknowledgement with exponential resending + * scheme */ +static int bul_resend_exp(struct mipv6_bul_entry *bulentry) +{ + unsigned long now = jiffies; + + DEBUG(DBG_INFO, "(0x%x) resending bu", (int) bulentry); + + + /* If sending a de-registration, do not care about the + * lifetime value, as de-registrations are normally sent with + * a zero lifetime value. If the entry is a home entry get the + * current lifetime. + */ + + if (bulentry->lifetime != 0) { + bulentry->lifetime = mipv6_mn_get_bulifetime( + &bulentry->home_addr, &bulentry->coa, bulentry->flags); + + bulentry->expire = now + bulentry->lifetime * HZ; + } else { + bulentry->expire = now + HOME_RESEND_EXPIRE * HZ; + } + if (bulentry->rr) { + /* Redo RR, if cookies have expired */ + if (time_after(jiffies, bulentry->rr->home_time + MAX_TOKEN_LIFE * HZ)) + bulentry->rr->rr_state |= RR_WAITH; + if (time_after(jiffies, bulentry->rr->careof_time + MAX_NONCE_LIFE * HZ)) + bulentry->rr->rr_state |= RR_WAITC; + + if (bulentry->rr->rr_state & RR_WAITH) { + /* Resend HoTI directly */ + mipv6_send_addr_test_init(&bulentry->home_addr, + &bulentry->cn_addr, MIPV6_MH_HOTI, + bulentry->rr->hot_cookie); + } + if (bulentry->rr->rr_state & RR_WAITC) { + /* Resend CoTI directly */ + mipv6_send_addr_test_init(&bulentry->coa, + &bulentry->cn_addr, MIPV6_MH_COTI, + bulentry->rr->cot_cookie); + } + goto out; + } + + bulentry->seq++; + + if (send_bu_msg(bulentry) < 0) + DEBUG(DBG_ERROR, "Resending of BU failed"); + +out: + /* Schedule next retransmission */ + if (bulentry->delay < bulentry->maxdelay) { + bulentry->delay = 2 * bulentry->delay; + if (bulentry->delay > bulentry->maxdelay) { + /* can happen if maxdelay is not power(mindelay, 2) */ + bulentry->delay = bulentry->maxdelay; + } + } else if (bulentry->flags & MIPV6_BU_F_HOME) { + /* Home registration - continue sending BU at maxdelay rate */ + DEBUG(DBG_INFO, "Sending BU to HA after max ack wait time " + "reached(0x%x)", (int) bulentry); + bulentry->delay = bulentry->maxdelay; + } else if (!(bulentry->flags & MIPV6_BU_F_HOME)) { + /* Failed to get BA from a CN */ + bulentry->callback_time = now; + return -1; + } + + bulentry->callback_time = now + bulentry->delay * HZ; + return 0; +} + + + +/* Callback for sending a registration refresh BU + */ +static int bul_refresh(struct mipv6_bul_entry *bulentry) +{ + unsigned long now = jiffies; + + /* Refresh interval passed, send new BU */ + DEBUG(DBG_INFO, "bul entry 0x%x refresh interval passed, sending new BU", (int) bulentry); + if (bulentry->lifetime == 0) + return 0; + + /* Set new maximum lifetime and expiration time */ + bulentry->lifetime = mipv6_mn_get_bulifetime(&bulentry->home_addr, + &bulentry->coa, + bulentry->flags); + bulentry->expire = now + bulentry->lifetime * HZ; + bulentry->seq++; + /* Send update */ + if (send_bu_msg(bulentry) < 0) + DEBUG(DBG_ERROR, "Resending of BU failed"); + + if (time_after_eq(now, bulentry->expire)) { + /* Sanity check */ + DEBUG(DBG_ERROR, "bul entry expire time in history - setting expire to %u secs", ERROR_DEF_LIFETIME); + bulentry->lifetime = ERROR_DEF_LIFETIME; + bulentry->expire = now + ERROR_DEF_LIFETIME*HZ; + } + + /* Set up retransmission */ + bulentry->state = RESEND_EXP; + bulentry->callback = bul_resend_exp; + bulentry->callback_time = now + INITIAL_BINDACK_TIMEOUT*HZ; + bulentry->delay = INITIAL_BINDACK_TIMEOUT; + bulentry->maxdelay = MAX_BINDACK_TIMEOUT; + + return 0; +} + +static int mipv6_send_RR_bu(struct mipv6_bul_entry *bulentry) +{ + int ret; + int ops_len = 0; + u16 nonces[2]; + + DEBUG(DBG_INFO, "Sending BU to CN %x:%x:%x:%x:%x:%x:%x:%x " + "for home address %x:%x:%x:%x:%x:%x:%x:%x", + NIPV6ADDR(&bulentry->cn_addr), NIPV6ADDR(&bulentry->home_addr)); + nonces[0] = bulentry->rr->home_nonce_index; + nonces[1] = bulentry->rr->careof_nonce_index; + ops_len = sizeof(struct mipv6_mo_bauth_data) + MIPV6_RR_MAC_LENGTH + + sizeof(struct mipv6_mo_nonce_indices); + if (bulentry->ops) { + DEBUG(DBG_WARNING, "Bul entry had existing mobility options, freeing them"); + kfree(bulentry->ops); + } + bulentry->ops = alloc_mh_opts(ops_len); + + if (!bulentry->ops) + return -ENOMEM; + if (append_mh_opt(bulentry->ops, MIPV6_OPT_NONCE_INDICES, + sizeof(struct mipv6_mo_nonce_indices) - 2, nonces) < 0) + return -ENOMEM; + + if (append_mh_opt(bulentry->ops, MIPV6_OPT_AUTH_DATA, + MIPV6_RR_MAC_LENGTH, NULL) < 0) + return -ENOMEM; + /* RR procedure is over, send a BU */ + if (!(bulentry->flags & MIPV6_BU_F_ACK)) { + DEBUG(DBG_INFO, "Setting bul callback to bul_entry_expired"); + bulentry->state = ACK_OK; + bulentry->callback = bul_entry_expired; + bulentry->callback_time = jiffies + HZ * bulentry->lifetime; + bulentry->expire = jiffies + HZ * bulentry->lifetime; + } + else { + bulentry->callback_time = jiffies + HZ; + bulentry->expire = jiffies + HZ * bulentry->lifetime; + } + + ret = send_bu_msg(bulentry); + mipv6_bul_reschedule(bulentry); + return ret; +} + +static int mipv6_rr_state(struct mipv6_bul_entry *bul, struct in6_addr *saddr, + struct in6_addr *coa, __u8 flags) +{ + if (!rr_configured) + return NO_RR; + if (flags & MIPV6_BU_F_HOME) { + /* We don't need RR, this is a Home Registration */ + return NO_RR; + } + if (!bul || !bul->rr) { + /* First time BU to CN, need RR */ + return DO_RR; + } + + switch (bul->rr->rr_state) { + case RR_INIT: + /* Need RR if first BU to CN */ + return DO_RR; + case RR_DONE: + /* If MN moves to a new coa, do RR for it */ + if (!ipv6_addr_cmp(&bul->coa, coa)) + return NO_RR; + else + return DO_RR; + default: + /* + * We are in the middle of RR, the HoTI and CoTI have been + * sent. But we haven't got HoT and CoT from the CN, so + * don't do anything more at this time. + */ + return INPROGRESS_RR; + } +} + +/** + * mipv6_RR_start - Start Return Routability procedure + * @home_addr: home address + * @cn_addr: correspondent address + * @coa: care-of address + * @entry: binding update list entry (if any) + * @initdelay: initial ack timeout + * @maxackdelay: maximum ack timeout + * @flags: flags + * @lifetime: lifetime of binding + * @ops: mobility options + * + * Caller must hold @bul_lock (write). + **/ +static int mipv6_RR_start(struct in6_addr *home_addr, struct in6_addr *cn_addr, + struct in6_addr *coa, struct mipv6_bul_entry *entry, + __u32 initdelay, __u32 maxackdelay, __u8 flags, + __u32 lifetime, struct mipv6_mh_opt *ops) +{ + int ret = -1; + struct mipv6_bul_entry *bulentry = entry; + struct mipv6_rr_info *rr = NULL; + int seq = 0; + DEBUG_FUNC(); + + /* Do RR procedure only for care-of address after handoff, + if home cookie is still valid */ + if (bulentry && bulentry->rr) { + if (time_before(jiffies, bulentry->rr->home_time + MAX_NONCE_LIFE * HZ) && + lifetime && !(ipv6_addr_cmp(home_addr, coa) == 0)) { + mipv6_rr_mn_cookie_create(bulentry->rr->cot_cookie); + DEBUG(DBG_INFO, "Bul entry and rr info exist, only doing RR for CoA"); + ipv6_addr_copy(&bulentry->coa, coa); + bulentry->rr->rr_state |= RR_WAITC; + } else if (!lifetime) { /* Send only HoTi when returning home */ + mipv6_rr_mn_cookie_create(bulentry->rr->hot_cookie); + DEBUG(DBG_INFO, "Bul entry and rr info exist, only doing RR for HoA"); + ipv6_addr_copy(&bulentry->coa, coa); /* Home address as CoA */ + bulentry->rr->rr_state |= RR_WAITH; + } + } else { + DEBUG(DBG_INFO, "Doing RR for both HoA and CoA"); + rr = kmalloc(sizeof(*rr), GFP_ATOMIC); + memset(rr, 0, sizeof(*rr)); + rr->rr_state = RR_WAITHC; + } + if (bulentry) { + if (bulentry->state == ACK_ERROR) + goto out; + seq = bulentry->seq + 1; + } else + seq = 0; + /* Save the info in the BUL to retransmit the BU after RR is done */ + /* Caller must hold bul_lock (write) since we don't */ + + if ((bulentry = mipv6_bul_add(cn_addr, home_addr, coa, + min_t(__u32, lifetime, MAX_RR_BINDING_LIFE), + seq, flags, bul_resend_exp, initdelay, + RESEND_EXP, initdelay, + maxackdelay, ops, + rr)) == NULL) { + DEBUG(DBG_INFO, "couldn't update BUL for HoTi"); + goto out; + } + + rr = bulentry->rr; + if (rr->rr_state&RR_WAITH) + mipv6_send_addr_test_init(home_addr, cn_addr, MIPV6_MH_HOTI, + rr->hot_cookie); + if (ipv6_addr_cmp(home_addr, coa) && lifetime) + mipv6_send_addr_test_init(coa, cn_addr, MIPV6_MH_COTI, rr->cot_cookie); + else { + bulentry->rr->rr_state &= ~RR_WAITC; + } + ret = 0; +out: + return ret; +} + +/* + * Status codes for mipv6_ba_rcvd() + */ +#define STATUS_UPDATE 0 +#define STATUS_REMOVE 1 + +/** + * mipv6_ba_rcvd - Update BUL for this Binding Acknowledgement + * @ifindex: interface BA came from + * @cnaddr: sender IPv6 address + * @home_addr: home address + * @sequence: sequence number + * @lifetime: lifetime granted by Home Agent in seconds + * @refresh: recommended resend interval + * @status: %STATUS_UPDATE (ack) or %STATUS_REMOVE (nack) + * + * This function must be called to notify the module of the receipt of + * a binding acknowledgement so that it can cease retransmitting the + * option. The caller must have validated the acknowledgement before calling + * this function. 'status' can be either STATUS_UPDATE in which case the + * binding acknowledgement is assumed to be valid and the corresponding + * binding update list entry is updated, or STATUS_REMOVE in which case + * the corresponding binding update list entry is removed (this can be + * used upon receiving a negative acknowledgement). + * Returns 0 if a matching binding update has been sent or non-zero if + * not. + */ +static int mipv6_ba_rcvd(int ifindex, struct in6_addr *cnaddr, + struct in6_addr *home_addr, + u16 sequence, u32 lifetime, + u32 refresh, int status) +{ + struct mipv6_bul_entry *bulentry; + unsigned long now = jiffies; + struct in6_addr coa; + + DEBUG(DBG_INFO, "BA received with sequence number 0x%x, status: %d", + (int) sequence, status); + + /* Find corresponding entry in binding update list. */ + write_lock(&bul_lock); + if ((bulentry = mipv6_bul_get(cnaddr, home_addr)) == NULL) { + DEBUG(DBG_INFO, "- discarded, no entry in bul matches BA source address"); + write_unlock(&bul_lock); + return -1; + } + + ipv6_addr_copy(&coa, &bulentry->coa); + if (status == SEQUENCE_NUMBER_OUT_OF_WINDOW) { + __u32 lifetime = mipv6_mn_get_bulifetime(&bulentry->home_addr, + &bulentry->coa, + bulentry->flags); + bulentry->seq = sequence; + + mipv6_send_bu(&bulentry->home_addr, &bulentry->cn_addr, + &bulentry->coa, INITIAL_BINDACK_TIMEOUT, + MAX_BINDACK_TIMEOUT, 1, bulentry->flags, + lifetime, NULL); + write_unlock(&bul_lock); + return 0; + } else if (status >= REASON_UNSPECIFIED) { + int err; + int at_home = MN_NOT_AT_HOME; + DEBUG(DBG_WARNING, "- NACK - BA status: %d, deleting bul entry", status); + if (bulentry->flags & MIPV6_BU_F_HOME) { + struct mn_info *minfo; + read_lock(&mn_info_lock); + minfo = mipv6_mninfo_get_by_home(home_addr); + if (minfo) { + spin_lock(&minfo->lock); + if (minfo->is_at_home != MN_NOT_AT_HOME) + minfo->is_at_home = MN_AT_HOME; + at_home = minfo->is_at_home; + minfo->has_home_reg = 0; + spin_unlock(&minfo->lock); + } + read_unlock(&mn_info_lock); + DEBUG(DBG_ERROR, "Home registration failed: BA status: %d, deleting bul entry", status); + } + write_unlock(&bul_lock); + err = mipv6_bul_delete(cnaddr, home_addr); + if (at_home == MN_AT_HOME) { + mipv6_mn_send_home_na(home_addr); + write_lock_bh(&bul_lock); + mipv6_bul_iterate(mn_cn_handoff, &coa); + write_unlock_bh(&bul_lock); + } + return err; + } + bulentry->state = ACK_OK; + + if (bulentry->flags & MIPV6_BU_F_HOME && lifetime > 0) { + /* For home registrations: schedule a refresh binding update. + * Use the refresh interval given by home agent or 80% + * of lifetime, whichever is less. + * + * Adjust binding lifetime if 'granted' lifetime + * (lifetime value in received binding acknowledgement) + * is shorter than 'requested' lifetime (lifetime + * value sent in corresponding binding update). + * max((L_remain - (L_update - L_ack)), 0) + */ + if (lifetime * HZ < (bulentry->expire - bulentry->lastsend)) { + bulentry->expire = + max_t(__u32, bulentry->expire - + ((bulentry->expire - bulentry->lastsend) - + lifetime * HZ), jiffies + + ERROR_DEF_LIFETIME * HZ); + } + if (refresh > lifetime || refresh == 0) + refresh = 4 * lifetime / 5; + DEBUG(DBG_INFO, "setting callback for expiration of" + " a Home Registration: lifetime:%d, refresh:%d", + lifetime, refresh); + bulentry->callback = bul_refresh; + bulentry->callback_time = now + refresh * HZ; + bulentry->expire = now + lifetime * HZ; + bulentry->lifetime = lifetime; + if (time_after_eq(jiffies, bulentry->expire)) { + /* Sanity check */ + DEBUG(DBG_ERROR, "bul entry expire time in history - setting expire to %u secs", + ERROR_DEF_LIFETIME); + bulentry->expire = jiffies + ERROR_DEF_LIFETIME * HZ; + } + mipv6_mn_set_home_reg(home_addr, 1); + mipv6_bul_iterate(mn_cn_handoff, &coa); + } else if ((bulentry->flags & MIPV6_BU_F_HOME) && bulentry->lifetime == 0) { + write_unlock(&bul_lock); + DEBUG(DBG_INFO, "Got BA for deregistration BU"); + mipv6_mn_set_home_reg(home_addr, 0); + mipv6_bul_delete(cnaddr, home_addr); + mipv6_mn_send_home_na(home_addr); + + write_lock_bh(&bul_lock); + mipv6_bul_iterate(mn_cn_handoff, &coa); + write_unlock_bh(&bul_lock); + return 0; + } + + mipv6_bul_reschedule(bulentry); + write_unlock(&bul_lock); + + return 0; +} + +static int mipv6_handle_mh_HC_test(struct sk_buff *skb, + struct in6_addr *saddr, + struct in6_addr *fcoa, + struct in6_addr *cn, + struct in6_addr *lcoa, + struct mipv6_mh *mh) +{ + int ret = 0; + int msg_len = (mh->length+1) << 3; + int opt_len; + + struct mipv6_mh_addr_test *tm = (struct mipv6_mh_addr_test *)mh->data; + struct mipv6_bul_entry *bulentry; + + DEBUG_FUNC(); + + if (msg_len > skb->len) + return -1; + + opt_len = msg_len - sizeof(*mh) - sizeof(*tm); + + if (opt_len < 0) { + __u32 pos = (__u32)&mh->length - (__u32)skb->nh.raw; + icmpv6_send(skb, ICMPV6_PARAMPROB, + ICMPV6_HDR_FIELD, pos, skb->dev); + + DEBUG(DBG_INFO, "Mobility Header length less than H/C Test"); + return -1; + } + if (fcoa || lcoa) { + DEBUG(DBG_INFO, "H/C Test has HAO or RTH2, dropped."); + return -1; + } + write_lock(&bul_lock); + + /* We need to get the home address, since CoT only has the CoA*/ + if (mh->type == MIPV6_MH_COT) { + if ((bulentry = mipv6_bul_get_by_ccookie(cn, tm->init_cookie)) == NULL) { + DEBUG(DBG_ERROR, "has no BUL or RR state for " + "source:%x:%x:%x:%x:%x:%x:%x:%x", + NIPV6ADDR(cn)); + write_unlock(&bul_lock); + return -1; + } + } else { /* HoT has the home address */ + if (((bulentry = mipv6_bul_get(cn, saddr)) == NULL) || !bulentry->rr) { + DEBUG(DBG_ERROR, "has no BUL or RR state for " + "source:%x:%x:%x:%x:%x:%x:%x:%x " + "dest:%x:%x:%x:%x:%x:%x:%x:%x", + NIPV6ADDR(cn), NIPV6ADDR(saddr)); + write_unlock(&bul_lock); + return -1; + } + } + + switch (mh->type) { + case MIPV6_MH_HOT: + if ((bulentry->rr->rr_state & RR_WAITH) == 0) { + DEBUG(DBG_ERROR, "Not waiting for a Home Test message"); + goto out; + } + /* + * Make sure no home cookies have been received yet. + * TODO: Check not being put in at this time since subsequent + * BU's after this time will have home cookie stored. + */ + + /* Check if the cookie received is the right one */ + if (!mipv6_equal_cookies(tm->init_cookie, + bulentry->rr->hot_cookie)) { + /* Invalid cookie, might be an old cookie */ + DEBUG(DBG_WARNING, "Received HoT cookie does not match stored cookie"); + goto out; + } + DEBUG(DBG_INFO, "Got Care-of Test message"); + bulentry->rr->rr_state &= ~RR_WAITH; + memcpy(bulentry->rr->home_cookie, tm->kgen_token, MIPV6_COOKIE_LEN); + bulentry->rr->home_nonce_index = tm->nonce_index; + bulentry->rr->home_time = jiffies; + ret = 1; + break; + + case MIPV6_MH_COT: + if ((bulentry->rr->rr_state & RR_WAITC) == 0) { + DEBUG(DBG_ERROR, "Not waiting for a Home Test message"); + goto out; + } + /* + * Make sure no home cookies have been received yet. + * TODO: Check not being put in at this time since subsequent + * BU's at this time will have careof cookie stored. + */ + + /* Check if the cookie received is the right one */ + if (!mipv6_equal_cookies(tm->init_cookie, + bulentry->rr->cot_cookie)) { + DEBUG(DBG_INFO, "Received CoT cookie does not match stored cookie"); + goto out; + } + bulentry->rr->rr_state &= ~RR_WAITC; + memcpy(bulentry->rr->careof_cookie, tm->kgen_token, MIPV6_COOKIE_LEN); + bulentry->rr->careof_nonce_index = tm->nonce_index; + bulentry->rr->careof_time = jiffies; + ret = 1; + break; + default: + /* Impossible to get here */ + break; + } +out: + if (bulentry->rr->rr_state == RR_DONE) { + if (bulentry->rr->kbu) /* First free any old keys */ + kfree(bulentry->rr->kbu); + /* Store the session key to be used in BU's */ + if (ipv6_addr_cmp(&bulentry->coa, &bulentry->home_addr) && bulentry->lifetime) + bulentry->rr->kbu = mipv6_rr_key_calc(bulentry->rr->home_cookie, + bulentry->rr->careof_cookie); + else + bulentry->rr->kbu = mipv6_rr_key_calc(bulentry->rr->home_cookie, + NULL); + /* RR procedure is over, send a BU */ + mipv6_send_RR_bu(bulentry); + } + write_unlock(&bul_lock); + return ret; +} + +/** + * mipv6_handle_mh_brr - Binding Refresh Request handler + * @home: home address + * @coa: care-of address + * @cn: source of this packet + * @mh: pointer to the beginning of the Mobility Header + * + * Handles Binding Refresh Request. Packet and offset to option are + * passed. Returns 0 on success, otherwise negative. + **/ +static int mipv6_handle_mh_brr(struct sk_buff *skb, + struct in6_addr *home, + struct in6_addr *unused1, + struct in6_addr *cn, + struct in6_addr *unused2, + struct mipv6_mh *mh) +{ + struct mipv6_mh_brr *brr = (struct mipv6_mh_brr *)mh->data; + struct mipv6_bul_entry *binding; + int msg_len = (mh->length+1) << 3; + int opt_len; + + if (msg_len > skb->len) + return -1; + + opt_len = msg_len - sizeof(*mh) - sizeof(*brr); + + if (opt_len < 0) { + __u32 pos = (__u32)&mh->length - (__u32)skb->nh.raw; + icmpv6_send(skb, ICMPV6_PARAMPROB, + ICMPV6_HDR_FIELD, pos, skb->dev); + + DEBUG(DBG_WARNING, "Mobility Header length less than BRR"); + MIPV6_INC_STATS(n_brr_drop.invalid); + return -1; + } + + /* check we know src, else drop */ + write_lock(&bul_lock); + if ((binding = mipv6_bul_get(cn, home)) == NULL) { + MIPV6_INC_STATS(n_brr_drop.misc); + write_unlock(&bul_lock); + return MH_UNKNOWN_CN; + } + + MIPV6_INC_STATS(n_brr_rcvd); + + if (opt_len > 0) { + struct mobopt opts; + memset(&opts, 0, sizeof(opts)); + if (parse_mo_tlv(brr + 1, opt_len, &opts) < 0) { + write_unlock(&bul_lock); + return -1; + } + /* + * MIPV6_OPT_AUTH_DATA + */ + } + + /* must hold bul_lock (write) */ + mipv6_RR_start(home, cn, &binding->coa, binding, binding->delay, + binding->maxdelay, binding->flags, + binding->lifetime, binding->ops); + + write_unlock(&bul_lock); + /* MAY also decide to delete binding and send zero lifetime BU + with alt-coa set to home address */ + + return 0; +} + +/** + * mipv6_handle_mh_ba - Binding Acknowledgement handler + * @src: source of this packet + * @coa: care-of address + * @home: home address + * @mh: pointer to the beginning of the Mobility Header + * + **/ +static int mipv6_handle_mh_ba(struct sk_buff *skb, + struct in6_addr *home, + struct in6_addr *coa, + struct in6_addr *src, + struct in6_addr *unused, + struct mipv6_mh *mh) +{ + struct mipv6_mh_ba *ba = (struct mipv6_mh_ba *)mh->data; + struct mipv6_bul_entry *binding = NULL; + struct mobopt opts; + int msg_len = (mh->length+1) << 3; + int opt_len; + + int auth = 1, req_auth = 1, refresh = -1, ifindex = 0; + u32 lifetime, sequence; + + if (msg_len > skb->len) + return -1; + + opt_len = msg_len - sizeof(*mh) - sizeof(*ba); + + if (opt_len < 0) { + __u32 pos = (__u32)&mh->length - (__u32)skb->nh.raw; + icmpv6_send(skb, ICMPV6_PARAMPROB, + ICMPV6_HDR_FIELD, pos, skb->dev); + + DEBUG(DBG_WARNING, "Mobility Header length less than BA"); + MIPV6_INC_STATS(n_ba_drop.invalid); + return -1; + } + + lifetime = ntohs(ba->lifetime) << 2; + sequence = ntohs(ba->sequence); + + if (opt_len > 0) { + memset(&opts, 0, sizeof(opts)); + if (parse_mo_tlv(ba + 1, opt_len, &opts) < 0) + return -1; + /* + * MIPV6_OPT_AUTH_DATA, MIPV6_OPT_BR_ADVICE + */ + if (opts.br_advice) + refresh = ntohs(opts.br_advice->refresh_interval); + } + + if (ba->status >= EXPIRED_HOME_NONCE_INDEX && + ba->status <= EXPIRED_NONCES) + req_auth = 0; + + write_lock(&bul_lock); + binding = mipv6_bul_get(src, home); + if (!binding) { + DEBUG(DBG_INFO, "No binding, BA dropped."); + write_unlock(&bul_lock); + return -1; + } + + if (opts.auth_data && binding->rr && + (mipv6_auth_check(src, coa, (__u8 *)mh, msg_len, + opts.auth_data, binding->rr->kbu) == 0)) + auth = 1; + + if (req_auth && binding->rr && !auth) { + DEBUG(DBG_INFO, "BA Authentication failed."); + MIPV6_INC_STATS(n_ba_drop.auth); + write_unlock(&bul_lock); + return MH_AUTH_FAILED; + } + + if (ba->status == SEQUENCE_NUMBER_OUT_OF_WINDOW) { + DEBUG(DBG_INFO, + "Sequence number out of window, setting seq to %d", + sequence); + } else if (binding->seq != sequence) { + DEBUG(DBG_INFO, "BU/BA Sequence Number mismatch %d != %d", + binding->seq, sequence); + MIPV6_INC_STATS(n_ba_drop.invalid); + write_unlock(&bul_lock); + return MH_SEQUENCE_MISMATCH; + } + if (ba->status == EXPIRED_HOME_NONCE_INDEX || ba->status == EXPIRED_NONCES) { + if (binding->rr) { + /* Need to resend home test init to CN */ + binding->rr->rr_state |= RR_WAITH; + mipv6_send_addr_test_init(&binding->home_addr, + &binding->cn_addr, + MIPV6_MH_HOTI, + binding->rr->hot_cookie); + MIPV6_INC_STATS(n_ban_rcvd); + } else { + DEBUG(DBG_WARNING, "Got BA with status EXPIRED_HOME_NONCE_INDEX" + "for non-RR BU"); + MIPV6_INC_STATS(n_ba_drop.invalid); + } + write_unlock(&bul_lock); + return 0; + } + if (ba->status == EXPIRED_CAREOF_NONCE_INDEX || ba->status == EXPIRED_NONCES) { + if (binding->rr) { + /* Need to resend care-of test init to CN */ + binding->rr->rr_state |= RR_WAITC; + mipv6_send_addr_test_init(&binding->coa, + &binding->cn_addr, + MIPV6_MH_COTI, + binding->rr->cot_cookie); + MIPV6_INC_STATS(n_ban_rcvd); + } else { + DEBUG(DBG_WARNING, "Got BA with status EXPIRED_HOME_CAREOF_INDEX" + "for non-RR BU"); + MIPV6_INC_STATS(n_ba_drop.invalid); + } + write_unlock(&bul_lock); + return 0; + } + write_unlock(&bul_lock); + + if (ba->status >= REASON_UNSPECIFIED) { + DEBUG(DBG_INFO, "Binding Ack status : %d indicates error", ba->status); + mipv6_ba_rcvd(ifindex, src, home, sequence, lifetime, + refresh, ba->status); + MIPV6_INC_STATS(n_ban_rcvd); + return 0; + } + MIPV6_INC_STATS(n_ba_rcvd); + if (mipv6_ba_rcvd(ifindex, src, home, ntohs(ba->sequence), lifetime, + refresh, ba->status)) { + DEBUG(DBG_WARNING, "mipv6_ba_rcvd failed"); + } + + return 0; +} + +/** + * mipv6_handle_mh_be - Binding Error handler + * @cn: source of this packet + * @coa: care-of address + * @home: home address + * @mh: pointer to the beginning of the Mobility Header + * + **/ + +static int mipv6_handle_mh_be(struct sk_buff *skb, + struct in6_addr *home, + struct in6_addr *coa, + struct in6_addr *cn, + struct in6_addr *unused, + struct mipv6_mh *mh) +{ + struct mipv6_mh_be *be = (struct mipv6_mh_be *)mh->data; + int msg_len = (mh->length+1) << 3; + int opt_len; + struct in6_addr *hoa; + struct bul_inval_args args; + + DEBUG_FUNC(); + + if (msg_len > skb->len) + return -1; + + opt_len = msg_len - sizeof(*mh) - sizeof(*be); + + if (opt_len < 0) { + __u32 pos = (__u32)&mh->length - (__u32)skb->nh.raw; + icmpv6_send(skb, ICMPV6_PARAMPROB, + ICMPV6_HDR_FIELD, pos, skb->dev); + + DEBUG(DBG_WARNING, "Mobility Header length less than BE"); + MIPV6_INC_STATS(n_be_drop.invalid); + return -1; + } + + + if (!ipv6_addr_any(&be->home_addr)) + hoa = &be->home_addr; + else + hoa = home; + + MIPV6_INC_STATS(n_be_rcvd); + + args.all_rr_states = 0; + args.cn = cn; + args.mn = hoa; + + switch (be->status) { + case 1: /* Home Address Option used without a binding */ + /* Get ULP information about CN-MN communication. If + nothing in progress, MUST delete. Otherwise MAY + ignore. */ + args.all_rr_states = 1; + case 2: /* Received unknown MH type */ + /* If not expecting ack, SHOULD ignore. If MH + extension in use, stop it. If not, stop RO for + this CN. */ + write_lock(&bul_lock); + mipv6_bul_iterate(mn_bul_invalidate, &args); + write_unlock(&bul_lock); + break; + } + + return 0; +} + +/* + * mipv6_bu_rate_limit() : Takes a bulentry, a COA and 'flags' to check + * whether BU being sent is for Home Registration or not. + * + * If the number of BU's sent is fewer than MAX_FAST_UPDATES, this BU + * is allowed to be sent at the MAX_UPDATE_RATE. + * If the number of BU's sent is greater than or equal to MAX_FAST_UPDATES, + * this BU is allowed to be sent at the SLOW_UPDATE_RATE. + * + * Assumption : This function is not re-entrant. and the caller holds the + * bulentry lock (by calling mipv6_bul_get()) to stop races with other + * CPU's executing this same function. + * + * Side-Effects. Either of the following could on success : + * 1. Sets consecutive_sends to 1 if the entry is a Home agent + * registration or the COA has changed. + * 2. Increments consecutive_sends if the number of BU's sent so + * far is less than MAX_FAST_UPDATES, and this BU is being sent + * atleast MAX_UPDATE_RATE after previous one. + * + * Return Value : 0 on Success, -1 on Failure + */ +static int mipv6_bu_rate_limit(struct mipv6_bul_entry *bulentry, + struct in6_addr *coa, __u8 flags) +{ + if ((flags & MIPV6_BU_F_HOME) || ipv6_addr_cmp(&bulentry->coa, coa)) { + /* Home Agent Registration or different COA - restart from 1 */ + bulentry->consecutive_sends = 1; + return 0; + } + + if (bulentry->consecutive_sends < MAX_FAST_UPDATES) { + /* First MAX_FAST_UPDATES can be sent at MAX_UPDATE_RATE */ + if (jiffies - bulentry->lastsend < MAX_UPDATE_RATE * HZ) { + return -1; + } + bulentry->consecutive_sends ++; + } else { + /* Remaining updates SHOULD be sent at SLOW_UPDATE_RATE */ + if (jiffies - bulentry->lastsend < SLOW_UPDATE_RATE * HZ) { + return -1; + } + /* Don't inc 'consecutive_sends' to avoid overflow to zero */ + } + /* OK to send a BU */ + return 0; +} + +/** + * mipv6_send_bu - send a Binding Update + * @saddr: source address for BU + * @daddr: destination address for BU + * @coa: care-of address for MN + * @initdelay: initial BA wait timeout + * @maxackdelay: maximum BA wait timeout + * @exp: exponention back off + * @flags: flags for BU + * @lifetime: granted lifetime for binding + * @ops: mobility options + * + * Send a binding update. 'flags' may contain any of %MIPV6_BU_F_ACK, + * %MIPV6_BU_F_HOME, %MIPV6_BU_F_ROUTER bitwise ORed. If + * %MIPV6_BU_F_ACK is included retransmission will be attempted until + * the update has been acknowledged. Retransmission is done if no + * acknowledgement is received within @initdelay seconds. @exp + * specifies whether to use exponential backoff (@exp != 0) or linear + * backoff (@exp == 0). For exponential backoff the time to wait for + * an acknowledgement is doubled on each retransmission until a delay + * of @maxackdelay, after which retransmission is no longer attempted. + * For linear backoff the delay is kept constant and @maxackdelay + * specifies the maximum number of retransmissions instead. If + * sub-options are present ops must contain all sub-options to be + * added. On a mobile node, use the mobile node's home address for + * @saddr. Returns 0 on success, non-zero on failure. + * + * Caller may not hold @bul_lock. + **/ +int mipv6_send_bu(struct in6_addr *saddr, struct in6_addr *daddr, + struct in6_addr *coa, u32 initdelay, + u32 maxackdelay, u8 exp, u8 flags, u32 lifetime, + struct mipv6_mh_opt *ops) +{ + int ret; + __u8 state; + __u16 seq = 0; + int (*callback)(struct mipv6_bul_entry *); + __u32 callback_time; + struct mipv6_bul_entry *bulentry; + + /* First a sanity check: don't send BU to local addresses */ + if(ipv6_chk_addr(daddr, NULL)) { + DEBUG(DBG_ERROR, "BUG: Trying to send BU to local address"); + return -1; + } + DEBUG(DBG_INFO, "Sending BU to CN %x:%x:%x:%x:%x:%x:%x:%x " + "for home address %x:%x:%x:%x:%x:%x:%x:%x", + NIPV6ADDR(daddr), NIPV6ADDR(saddr)); + + if ((bulentry = mipv6_bul_get(daddr, saddr)) != NULL) { + if (bulentry->state == ACK_ERROR) { + /* + * Don't send any more BU's to nodes which don't + * understanding one. + */ + DEBUG(DBG_INFO, "Not sending BU to node which doesn't" + " understand one"); + return -1; + } + if (mipv6_bu_rate_limit(bulentry, coa, flags) < 0) { + DEBUG(DBG_DATADUMP, "Limiting BU sent."); + return 0; + } + } + + switch (mipv6_rr_state(bulentry, saddr, coa, flags)) { + case INPROGRESS_RR: + /* We are already doing RR, don't do BU at this time, it is + * done automatically later */ + DEBUG(DBG_INFO, "RR in progress not sending BU"); + return 0; + + case DO_RR: + /* Just do RR and return, BU is done automatically later */ + DEBUG(DBG_INFO, "starting RR" ); + mipv6_RR_start(saddr, daddr, coa, bulentry, initdelay, + maxackdelay, flags, lifetime, ops); + return 0; + + case NO_RR: + DEBUG(DBG_DATADUMP, "No RR necessary" ); + default: + break; + } + + if (bulentry) + seq = bulentry->seq + 1; + + /* Add to binding update list */ + + if (flags & MIPV6_BU_F_ACK) { + DEBUG(DBG_INFO, "Setting bul callback to bul_resend_exp"); + /* Send using exponential backoff */ + state = RESEND_EXP; + callback = bul_resend_exp; + callback_time = initdelay; + } else { + DEBUG(DBG_INFO, "Setting bul callback to bul_entry_expired"); + /* No acknowledgement/resending required */ + state = ACK_OK; /* pretend we got an ack */ + callback = bul_entry_expired; + callback_time = lifetime; + } + + /* BU only for the home address */ + /* We must hold bul_lock (write) while calling add */ + if ((bulentry = mipv6_bul_add(daddr, saddr, coa, lifetime, seq, + flags, callback, callback_time, + state, initdelay, maxackdelay, ops, + NULL)) == NULL) { + DEBUG(DBG_INFO, "couldn't update BUL"); + return 0; + } + ret = send_bu_msg(bulentry); + + return ret; +} + +int __init mipv6_mh_mn_init(void) +{ + mipv6_mh_register(MIPV6_MH_HOT, mipv6_handle_mh_HC_test); + mipv6_mh_register(MIPV6_MH_COT, mipv6_handle_mh_HC_test); + mipv6_mh_register(MIPV6_MH_BA, mipv6_handle_mh_ba); + mipv6_mh_register(MIPV6_MH_BRR, mipv6_handle_mh_brr); + mipv6_mh_register(MIPV6_MH_BE, mipv6_handle_mh_be); + + return 0; +} + +void __exit mipv6_mh_mn_exit(void) +{ + mipv6_mh_unregister(MIPV6_MH_HOT); + mipv6_mh_unregister(MIPV6_MH_COT); + mipv6_mh_unregister(MIPV6_MH_BA); + mipv6_mh_unregister(MIPV6_MH_BRR); + mipv6_mh_unregister(MIPV6_MH_BE); +} diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/module_cn.c linux-2.4.25/net/ipv6/mobile_ip6/module_cn.c --- linux-2.4.25.old/net/ipv6/mobile_ip6/module_cn.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.25/net/ipv6/mobile_ip6/module_cn.c 2004-06-26 11:29:31.000000000 +0100 @@ -0,0 +1,167 @@ +/* + * Mobile IPv6 Common Module + * + * Authors: + * Sami Kivisaari + * Antti Tuominen + * + * $Id$ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include + +#ifdef CONFIG_SYSCTL +#include +#endif /* CONFIG_SYSCTL */ + +#include + +#include "bcache.h" +#include "mipv6_icmp.h" +#include "stats.h" +#include "mobhdr.h" +#include "exthdrs.h" + +int mipv6_debug = 1; + +#if defined(MODULE) && LINUX_VERSION_CODE > 0x20115 +MODULE_AUTHOR("MIPL Team"); +MODULE_DESCRIPTION("Mobile IPv6"); +MODULE_LICENSE("GPL"); +MODULE_PARM(mipv6_debug, "i"); +#endif + +#include "config.h" + +struct mip6_func mip6_fn; +struct mip6_conf mip6node_cnf = { + capabilities: CAP_CN, + accept_ret_rout: 1, + max_rtr_reachable_time: 0, + eager_cell_switching: 0, + max_num_tunnels: 0, + min_num_tunnels: 0, + binding_refresh_advice: 0, + bu_lladdr: 0, + bu_keymgm: 0, + bu_cn_ack: 0 +}; + +#define MIPV6_BCACHE_SIZE 128 + +/********************************************************************** + * + * MIPv6 CN Module Init / Cleanup + * + **********************************************************************/ + +#ifdef CONFIG_SYSCTL +/* Sysctl table */ +ctl_table mipv6_mobility_table[] = { + {NET_IPV6_MOBILITY_DEBUG, "debuglevel", + &mipv6_debug, sizeof(int), 0644, NULL, + &proc_dointvec}, + {NET_IPV6_MOBILITY_RETROUT, "accept_return_routability", + &mip6node_cnf.accept_ret_rout, sizeof(int), 0644, NULL, + &proc_dointvec}, + {0} +}; +ctl_table mipv6_table[] = { + {NET_IPV6_MOBILITY, "mobility", NULL, 0, 0555, mipv6_mobility_table}, + {0} +}; + +static struct ctl_table_header *mipv6_sysctl_header; +static struct ctl_table mipv6_net_table[]; +static struct ctl_table mipv6_root_table[]; + +ctl_table mipv6_net_table[] = { + {NET_IPV6, "ipv6", NULL, 0, 0555, mipv6_table}, + {0} +}; + +ctl_table mipv6_root_table[] = { + {CTL_NET, "net", NULL, 0, 0555, mipv6_net_table}, + {0} +}; +#endif /* CONFIG_SYSCTL */ + +extern void mipv6_rr_init(void); + +/* Initialize the module */ +static int __init mip6_init(void) +{ + int err = 0; + + printk(KERN_INFO "MIPL Mobile IPv6 for Linux Correspondent Node %s (%s)\n", + MIPLVERSION, MIPV6VERSION); + +#ifdef CONFIG_IPV6_MOBILITY_DEBUG + printk(KERN_INFO "Debug-level: %d\n", mipv6_debug); +#endif + + if ((err = mipv6_bcache_init(MIPV6_BCACHE_SIZE)) < 0) + goto bcache_fail; + + if ((err = mipv6_icmpv6_init()) < 0) + goto icmp_fail; + + if ((err = mipv6_stats_init()) < 0) + goto stats_fail; + mipv6_rr_init(); + +#ifdef CONFIG_SYSCTL + mipv6_sysctl_header = register_sysctl_table(mipv6_root_table, 0); +#endif + + if ((err = mipv6_mh_common_init()) < 0) + goto mh_fail; + + MIPV6_SETCALL(mipv6_modify_txoptions, mipv6_modify_txoptions); + + MIPV6_SETCALL(mipv6_handle_homeaddr, mipv6_handle_homeaddr); + MIPV6_SETCALL(mipv6_icmp_swap_addrs, mipv6_icmp_swap_addrs); + + return 0; + +mh_fail: +#ifdef CONFIG_SYSCTL + unregister_sysctl_table(mipv6_sysctl_header); +#endif + mipv6_stats_exit(); +stats_fail: + mipv6_icmpv6_exit(); +icmp_fail: + mipv6_bcache_exit(); +bcache_fail: + return err; +} +module_init(mip6_init); + +#ifdef MODULE +/* Cleanup module */ +static void __exit mip6_exit(void) +{ + printk(KERN_INFO "mip6_base.o exiting.\n"); +#ifdef CONFIG_SYSCTL + unregister_sysctl_table(mipv6_sysctl_header); +#endif + + /* Invalidate all custom kernel hooks. No need to do this + separately for all hooks. */ + mipv6_invalidate_calls(); + + mipv6_mh_common_exit(); + mipv6_stats_exit(); + mipv6_icmpv6_exit(); + mipv6_bcache_exit(); +} +module_exit(mip6_exit); +#endif /* MODULE */ diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/module_ha.c linux-2.4.25/net/ipv6/mobile_ip6/module_ha.c --- linux-2.4.25.old/net/ipv6/mobile_ip6/module_ha.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.25/net/ipv6/mobile_ip6/module_ha.c 2004-06-26 11:29:31.000000000 +0100 @@ -0,0 +1,264 @@ +/* + * Mobile IPv6 Home Agent Module + * + * Authors: + * Sami Kivisaari + * Antti Tuominen + * + * $Id$ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include + +#ifdef CONFIG_SYSCTL +#include +#endif /* CONFIG_SYSCTL */ + +#include +#include + +#include "mobhdr.h" +#include "tunnel_ha.h" +#include "ha.h" +#include "halist.h" +#include "mipv6_icmp.h" +//#include "prefix.h" +#include "bcache.h" +#include "debug.h" + +int mipv6_use_auth = 0; + +#if defined(MODULE) && LINUX_VERSION_CODE > 0x20115 +MODULE_AUTHOR("MIPL Team"); +MODULE_DESCRIPTION("Mobile IPv6 Home Agent"); +MODULE_LICENSE("GPL"); +#endif + +#include "config.h" + +#define MIPV6_HALIST_SIZE 128 +struct ha_info_opt { + u8 type; + u8 len; + u16 res; + u16 pref; + u16 ltime; +}; +/* + * Called from ndisc.c's router_discovery. + */ +static int mipv6_ha_ra_rcv(struct sk_buff *skb, struct ndisc_options *ndopts) +{ + unsigned int ha_info_pref = 0, ha_info_lifetime; + int ifi = ((struct inet6_skb_parm *)skb->cb)->iif; + struct ra_msg *ra = (struct ra_msg *) skb->h.raw; + struct in6_addr *saddr = &skb->nh.ipv6h->saddr; + struct in6_addr ll_addr; + struct hal { + struct in6_addr prefix; + int plen; + struct hal *next; + }; + + DEBUG_FUNC(); + + ha_info_lifetime = ntohs(ra->icmph.icmp6_rt_lifetime); + ipv6_addr_copy(&ll_addr, saddr); + + if (ndopts->nd_opts_hai) { + struct ha_info_opt *hai = (struct ha_info_opt *)ndopts->nd_opts_hai; + ha_info_pref = ntohs(hai->pref); + ha_info_lifetime = ntohs(hai->ltime); + DEBUG(DBG_DATADUMP, + "received home agent info with preference : %d and lifetime : %d", + ha_info_pref, ha_info_lifetime); + } + if (ndopts->nd_opts_pi) { + struct nd_opt_hdr *p; + for (p = ndopts->nd_opts_pi; + p; + p = ndisc_next_option(p, ndopts->nd_opts_pi_end)) { + struct prefix_info *pinfo; + + pinfo = (struct prefix_info *) p; + + if (pinfo->router_address) { + DEBUG(DBG_DATADUMP, "Adding router address to " + "ha queue \n"); + /* If RA has H bit set and Prefix Info + * Option R bit set, queue this + * address to be added to Home Agents + * List. + */ + if (ipv6_addr_type(&pinfo->prefix) & + IPV6_ADDR_LINKLOCAL) + continue; + if (!ra->icmph.icmp6_home_agent || !ha_info_lifetime) { + mipv6_halist_delete(&pinfo->prefix); + continue; + } else { + + mipv6_halist_add(ifi, &pinfo->prefix, + pinfo->prefix_len, &ll_addr, + ha_info_pref, ha_info_lifetime); + } + + } + + } + } + return MIPV6_ADD_RTR; +} + +/********************************************************************** + * + * MIPv6 Module Init / Cleanup + * + **********************************************************************/ + +#ifdef CONFIG_SYSCTL +/* Sysctl table */ +extern int +mipv6_max_tnls_sysctl(ctl_table *, int, struct file *, void *, size_t *); + +extern int +mipv6_min_tnls_sysctl(ctl_table *, int, struct file *, void *, size_t *); + +int max_adv = ~(u16)0; +int min_zero = 0; +ctl_table mipv6_mobility_table[] = { + {NET_IPV6_MOBILITY_BINDING_REFRESH, "binding_refresh_advice", + &mip6node_cnf.binding_refresh_advice, sizeof(int), 0644, NULL, + &proc_dointvec_minmax, &sysctl_intvec, 0, &min_zero, &max_adv}, + + {NET_IPV6_MOBILITY_MAX_TNLS, "max_tnls", &mipv6_max_tnls, sizeof(int), + 0644, NULL, &mipv6_max_tnls_sysctl}, + {NET_IPV6_MOBILITY_MIN_TNLS, "min_tnls", &mipv6_min_tnls, sizeof(int), + 0644, NULL, &mipv6_min_tnls_sysctl}, + {0} +}; +ctl_table mipv6_table[] = { + {NET_IPV6_MOBILITY, "mobility", NULL, 0, 0555, mipv6_mobility_table}, + {0} +}; + +static struct ctl_table_header *mipv6_sysctl_header; +static struct ctl_table mipv6_net_table[]; +static struct ctl_table mipv6_root_table[]; + +ctl_table mipv6_net_table[] = { + {NET_IPV6, "ipv6", NULL, 0, 0555, mipv6_table}, + {0} +}; + +ctl_table mipv6_root_table[] = { + {CTL_NET, "net", NULL, 0, 0555, mipv6_net_table}, + {0} +}; +#endif /* CONFIG_SYSCTL */ + +extern void mipv6_check_dad(struct in6_addr *haddr); +extern void mipv6_dad_init(void); +extern void mipv6_dad_exit(void); +extern int mipv6_forward(struct sk_buff *); + +/* Initialize the module */ +static int __init mip6_ha_init(void) +{ + int err = 0; + + printk(KERN_INFO "MIPL Mobile IPv6 for Linux Home Agent %s (%s)\n", + MIPLVERSION, MIPV6VERSION); + mip6node_cnf.capabilities = CAP_CN | CAP_HA; + + mip6_fn.icmpv6_dhaad_rep_rcv = mipv6_icmpv6_no_rcv; + mip6_fn.icmpv6_dhaad_req_rcv = mipv6_icmpv6_rcv_dhaad_req; + mip6_fn.icmpv6_pfxadv_rcv = mipv6_icmpv6_no_rcv; + mip6_fn.icmpv6_pfxsol_rcv = mipv6_icmpv6_no_rcv; + mip6_fn.icmpv6_paramprob_rcv = mipv6_icmpv6_no_rcv; + +#ifdef CONFIG_IPV6_MOBILITY_DEBUG + printk(KERN_INFO "Debug-level: %d\n", mipv6_debug); +#endif + +#ifdef CONFIG_SYSCTL + mipv6_sysctl_header = register_sysctl_table(mipv6_root_table, 0); +#endif + mipv6_initialize_tunnel(); + + if ((err = mipv6_ha_init()) < 0) + goto ha_fail; + + MIPV6_SETCALL(mipv6_ra_rcv, mipv6_ha_ra_rcv); + MIPV6_SETCALL(mipv6_forward, mipv6_forward); + mipv6_dad_init(); + MIPV6_SETCALL(mipv6_check_dad, mipv6_check_dad); + + if ((err = mipv6_halist_init(MIPV6_HALIST_SIZE)) < 0) + goto halist_fail; + +// mipv6_initialize_pfx_icmpv6(); + + return 0; + +halist_fail: + mipv6_dad_exit(); + mipv6_ha_exit(); +ha_fail: + mipv6_shutdown_tunnel(); + + mip6_fn.icmpv6_dhaad_rep_rcv = NULL; + mip6_fn.icmpv6_dhaad_req_rcv = NULL; + mip6_fn.icmpv6_pfxadv_rcv = NULL; + mip6_fn.icmpv6_pfxsol_rcv = NULL; + mip6_fn.icmpv6_paramprob_rcv = NULL; + + MIPV6_RESETCALL(mipv6_ra_rcv); + MIPV6_RESETCALL(mipv6_forward); + MIPV6_RESETCALL(mipv6_check_dad); + +#ifdef CONFIG_SYSCTL + unregister_sysctl_table(mipv6_sysctl_header); +#endif + return err; +} +module_init(mip6_ha_init); + +#ifdef MODULE +/* Cleanup module */ +static void __exit mip6_ha_exit(void) +{ + printk(KERN_INFO "mip6_ha.o exiting.\n"); + mip6node_cnf.capabilities &= ~(int)CAP_HA; + + mipv6_bcache_cleanup(HOME_REGISTRATION); + + MIPV6_RESETCALL(mipv6_ra_rcv); + MIPV6_RESETCALL(mipv6_forward); + MIPV6_RESETCALL(mipv6_check_dad); + + mipv6_halist_exit(); +// mipv6_shutdown_pfx_icmpv6(); + + mip6_fn.icmpv6_dhaad_rep_rcv = NULL; + mip6_fn.icmpv6_dhaad_req_rcv = NULL; + mip6_fn.icmpv6_pfxadv_rcv = NULL; + mip6_fn.icmpv6_pfxsol_rcv = NULL; + mip6_fn.icmpv6_paramprob_rcv = NULL; + + mipv6_dad_exit(); + mipv6_ha_exit(); + mipv6_shutdown_tunnel(); +#ifdef CONFIG_SYSCTL + unregister_sysctl_table(mipv6_sysctl_header); +#endif +} +module_exit(mip6_ha_exit); +#endif /* MODULE */ diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/module_mn.c linux-2.4.25/net/ipv6/mobile_ip6/module_mn.c --- linux-2.4.25.old/net/ipv6/mobile_ip6/module_mn.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.25/net/ipv6/mobile_ip6/module_mn.c 2004-06-26 11:29:31.000000000 +0100 @@ -0,0 +1,188 @@ +/* + * Mobile IPv6 Mobile Node Module + * + * Authors: + * Sami Kivisaari + * Antti Tuominen + * + * $Id$ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include + +#ifdef CONFIG_SYSCTL +#include +#endif /* CONFIG_SYSCTL */ + +#include + +extern int mipv6_debug; +int mipv6_use_auth = 0; + +#if defined(MODULE) && LINUX_VERSION_CODE > 0x20115 +MODULE_AUTHOR("MIPL Team"); +MODULE_DESCRIPTION("Mobile IPv6 Mobile Node"); +MODULE_LICENSE("GPL"); +MODULE_PARM(mipv6_debug, "i"); +#endif + +#include "config.h" + +#include "mobhdr.h" +#include "mn.h" +#include "mipv6_icmp.h" +//#include "prefix.h" + +/* TODO: These will go as soon as we get rid of the last two ioctls */ +extern int mipv6_ioctl_mn_init(void); +extern void mipv6_ioctl_mn_exit(void); + +/********************************************************************** + * + * MIPv6 Module Init / Cleanup + * + **********************************************************************/ + +#ifdef CONFIG_SYSCTL +/* Sysctl table */ + +extern int max_rtr_reach_time; +extern int eager_cell_switching; + +static int max_reach = 1000; +static int min_reach = 1; +static int max_one = 1; +static int min_zero = 0; + +extern int +mipv6_mdetect_mech_sysctl(ctl_table *, int, struct file *, void *, size_t *); + +extern int +mipv6_router_reach_sysctl(ctl_table *, int, struct file *, void *, size_t *); + +ctl_table mipv6_mobility_table[] = { + {NET_IPV6_MOBILITY_BU_F_LLADDR, "bu_flag_lladdr", + &mip6node_cnf.bu_lladdr, sizeof(int), 0644, NULL, + &proc_dointvec_minmax, &sysctl_intvec, 0, &min_zero, &max_one}, + {NET_IPV6_MOBILITY_BU_F_KEYMGM, "bu_flag_keymgm", + &mip6node_cnf.bu_keymgm, sizeof(int), 0644, NULL, + &proc_dointvec_minmax, &sysctl_intvec, 0, &min_zero, &max_one}, + {NET_IPV6_MOBILITY_BU_F_CN_ACK, "bu_flag_cn_ack", + &mip6node_cnf.bu_cn_ack, sizeof(int), 0644, NULL, + &proc_dointvec_minmax, &sysctl_intvec, 0, &min_zero, &max_one}, + + {NET_IPV6_MOBILITY_ROUTER_REACH, "max_router_reachable_time", + &max_rtr_reach_time, sizeof(int), 0644, NULL, + &proc_dointvec_minmax, &sysctl_intvec, 0, &min_reach, &max_reach}, + + {NET_IPV6_MOBILITY_MDETECT_MECHANISM, "eager_cell_switching", + &eager_cell_switching, sizeof(int), 0644, NULL, + &proc_dointvec_minmax, &sysctl_intvec, 0, &min_zero, &max_one}, + + {0} +}; +ctl_table mipv6_table[] = { + {NET_IPV6_MOBILITY, "mobility", NULL, 0, 0555, mipv6_mobility_table}, + {0} +}; + +static struct ctl_table_header *mipv6_sysctl_header; +static struct ctl_table mipv6_net_table[]; +static struct ctl_table mipv6_root_table[]; + +ctl_table mipv6_net_table[] = { + {NET_IPV6, "ipv6", NULL, 0, 0555, mipv6_table}, + {0} +}; + +ctl_table mipv6_root_table[] = { + {CTL_NET, "net", NULL, 0, 0555, mipv6_net_table}, + {0} +}; +#endif /* CONFIG_SYSCTL */ + +/* Initialize the module */ +static int __init mip6_mn_init(void) +{ + int err = 0; + + printk(KERN_INFO "MIPL Mobile IPv6 for Linux Mobile Node %s (%s)\n", + MIPLVERSION, MIPV6VERSION); + mip6node_cnf.capabilities = CAP_CN | CAP_MN; + +#ifdef CONFIG_IPV6_MOBILITY_DEBUG + printk(KERN_INFO "Debug-level: %d\n", mipv6_debug); +#endif + +#ifdef CONFIG_SYSCTL + mipv6_sysctl_header = register_sysctl_table(mipv6_root_table, 0); +#endif + if ((err = mipv6_mn_init()) < 0) + goto mn_fail; + + mipv6_mh_mn_init(); + + mip6_fn.icmpv6_dhaad_rep_rcv = mipv6_icmpv6_rcv_dhaad_rep; + mip6_fn.icmpv6_dhaad_req_rcv = mipv6_icmpv6_no_rcv; + mip6_fn.icmpv6_pfxadv_rcv = mipv6_icmpv6_no_rcv; + mip6_fn.icmpv6_pfxsol_rcv = mipv6_icmpv6_no_rcv; + mip6_fn.icmpv6_paramprob_rcv = mipv6_icmpv6_rcv_paramprob; + +// mipv6_initialize_pfx_icmpv6(); + + if ((err = mipv6_ioctl_mn_init()) < 0) + goto ioctl_fail; + + return 0; + +ioctl_fail: +// mipv6_shutdown_pfx_icmpv6(); + + mip6_fn.icmpv6_dhaad_rep_rcv = NULL; + mip6_fn.icmpv6_dhaad_req_rcv = NULL; + mip6_fn.icmpv6_pfxadv_rcv = NULL; + mip6_fn.icmpv6_pfxsol_rcv = NULL; + mip6_fn.icmpv6_paramprob_rcv = NULL; + + mipv6_mh_mn_exit(); + mipv6_mn_exit(); +mn_fail: +#ifdef CONFIG_SYSCTL + unregister_sysctl_table(mipv6_sysctl_header); +#endif + return err; +} +module_init(mip6_mn_init); + +#ifdef MODULE +/* Cleanup module */ +static void __exit mip6_mn_exit(void) +{ + printk(KERN_INFO "mip6_mn.o exiting.\n"); + mip6node_cnf.capabilities &= ~(int)CAP_MN; + + mipv6_ioctl_mn_exit(); +// mipv6_shutdown_pfx_icmpv6(); + + mip6_fn.icmpv6_dhaad_rep_rcv = NULL; + mip6_fn.icmpv6_dhaad_req_rcv = NULL; + mip6_fn.icmpv6_pfxadv_rcv = NULL; + mip6_fn.icmpv6_pfxsol_rcv = NULL; + mip6_fn.icmpv6_paramprob_rcv = NULL; + + mipv6_mn_exit(); + +/* common cleanup */ +#ifdef CONFIG_SYSCTL + unregister_sysctl_table(mipv6_sysctl_header); +#endif +} +module_exit(mip6_mn_exit); +#endif /* MODULE */ diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/multiaccess_ctl.c linux-2.4.25/net/ipv6/mobile_ip6/multiaccess_ctl.c --- linux-2.4.25.old/net/ipv6/mobile_ip6/multiaccess_ctl.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.25/net/ipv6/mobile_ip6/multiaccess_ctl.c 2004-06-26 11:29:31.000000000 +0100 @@ -0,0 +1,287 @@ +/* + * 2001 (c) Oy L M Ericsson Ab + * + * Author: NomadicLab / Ericsson Research + * + * $Id$ + * + */ + +/* + * Vertical hand-off information manager + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "multiaccess_ctl.h" +#include "debug.h" + +/* + * Local variables + */ +static LIST_HEAD(if_list); + +/* Internal interface information list */ +struct ma_if_info { + struct list_head list; + int interface_id; + int preference; + __u8 status; +}; + +/** + * ma_ctl_get_preference - get preference value for interface + * @ifi: interface index + * + * Returns integer value preference for given interface. + **/ +int ma_ctl_get_preference(int ifi) +{ + struct list_head *lh; + struct ma_if_info *info; + int pref = 0; + + list_for_each(lh, &if_list) { + info = list_entry(lh, struct ma_if_info, list); + if (info->interface_id == ifi) { + pref = info->preference; + return pref; + } + } + return -1; +} +/** + * ma_ctl_get_preference - get preference value for interface + * @ifi: interface index + * + * Returns integer value interface index for interface with highest preference. + **/ +int ma_ctl_get_preferred_if(void) +{ + struct list_head *lh; + struct ma_if_info *info, *pref_if = NULL; + + list_for_each(lh, &if_list) { + info = list_entry(lh, struct ma_if_info, list); + if (!pref_if || (info->preference > pref_if->preference)) { + pref_if = info; + } + } + if (pref_if) return pref_if->interface_id; + return 0; +} +/** + * ma_ctl_set_preference - set preference for interface + * @arg: ioctl args + * + * Sets preference of an existing interface (called by ioctl). + **/ +void ma_ctl_set_preference(unsigned long arg) +{ + struct list_head *lh; + struct ma_if_info *info; + struct ma_if_uinfo uinfo; + + memset(&uinfo, 0, sizeof(struct ma_if_uinfo)); + if (copy_from_user(&uinfo, (struct ma_if_uinfo *)arg, + sizeof(struct ma_if_uinfo)) < 0) { + DEBUG(DBG_WARNING, "copy_from_user failed"); + return; + } + + /* check if the interface exists */ + list_for_each(lh, &if_list) { + info = list_entry(lh, struct ma_if_info, list); + if (info->interface_id == uinfo.interface_id) { + info->preference = uinfo.preference; + return; + } + } +} + +/** + * ma_ctl_add_iface - add new interface to list + * @if_index: interface index + * + * Adds new interface entry to preference list. Preference is set to + * the same value as @if_index. Entry @status is set to + * %MA_IFACE_NOT_USED. + **/ +void ma_ctl_add_iface(int if_index) +{ + struct list_head *lh; + struct ma_if_info *info; + + DEBUG_FUNC(); + + /* check if the interface already exists */ + list_for_each(lh, &if_list) { + info = list_entry(lh, struct ma_if_info, list); + if (info->interface_id == if_index) { + info->status = MA_IFACE_NOT_USED; + info->preference = if_index; + return; + } + } + + info = kmalloc(sizeof(struct ma_if_info), GFP_ATOMIC); + if (info == NULL) { + DEBUG(DBG_ERROR, "Out of memory"); + return; + } + memset(info, 0, sizeof(struct ma_if_info)); + info->interface_id = if_index; + info->preference = if_index; + info->status = MA_IFACE_NOT_USED; + list_add(&info->list, &if_list); +} + +/** + * ma_ctl_del_iface - remove entry from the list + * @if_index: interface index + * + * Removes entry for interface @if_index from preference list. + **/ +int ma_ctl_del_iface(int if_index) +{ + struct list_head *lh, *next; + struct ma_if_info *info; + + DEBUG_FUNC(); + + /* if the iface exists, change availability to 0 */ + list_for_each_safe(lh, next, &if_list) { + info = list_entry(lh, struct ma_if_info, list); + if (info->interface_id == if_index) { + list_del(&info->list); + kfree(info); + return 0; + } + } + + return -1; +} + +/** + * ma_ctl_upd_iface - update entry (and list) + * @if_index: interface to update + * @status: new status for interface + * @change_if_index: new interface + * + * Updates @if_index entry on preference list. Entry status is set to + * @status. If new @status is %MA_IFACE_CURRENT, updates list to have + * only one current device. If @status is %MA_IFACE_NOT_PRESENT, + * entry is deleted and further if entry had %MA_IFACE_CURRENT set, + * new current device is looked up and returned in @change_if_index. + * New preferred interface is also returned if current device changes + * to %MA_IFACE_NOT_USED. Returns 0 on success, otherwise negative. + **/ +int ma_ctl_upd_iface(int if_index, int status, int *change_if_index) +{ + struct list_head *lh, *tmp; + struct ma_if_info *info, *pref = NULL; + int found = 0; + + DEBUG_FUNC(); + + *change_if_index = 0; + + /* check if the interface exists */ + list_for_each_safe(lh, tmp, &if_list) { + info = list_entry(lh, struct ma_if_info, list); + if (status == MA_IFACE_NOT_PRESENT) { + if (info->interface_id == if_index) { + list_del_init(&info->list); + kfree(info); + found = 1; + break; + } + } else if (status == MA_IFACE_CURRENT) { + if (info->interface_id == if_index) { + info->status |= MA_IFACE_CURRENT; + found = 1; + } else { + info->status |= MA_IFACE_NOT_USED; + } + } else if (status == MA_IFACE_NOT_USED) { + if (info->interface_id == if_index) { + if (info->status | MA_IFACE_CURRENT) { + found = 1; + } + info->status &= !MA_IFACE_CURRENT; + info->status |= MA_IFACE_NOT_USED; + info->status &= !MA_IFACE_HAS_ROUTER; + } + break; + } else if (status == MA_IFACE_HAS_ROUTER) { + if (info->interface_id == if_index) { + info->status |= MA_IFACE_HAS_ROUTER; + } + return 0; + } + } + + if (status & (MA_IFACE_NOT_USED|MA_IFACE_NOT_PRESENT) && found) { + /* select new interface */ + list_for_each(lh, &if_list) { + info = list_entry(lh, struct ma_if_info, list); + if (pref == NULL || ((info->preference > pref->preference) && + info->status & MA_IFACE_HAS_ROUTER)) + pref = info; + } + if (pref) { + *change_if_index = pref->interface_id; + pref->status |= MA_IFACE_CURRENT; + } else { + *change_if_index = -1; + } + return 0; + } + + if (found) return 0; + + return -1; +} + +static int if_proc_info(char *buffer, char **start, off_t offset, + int length) +{ + struct list_head *lh; + struct ma_if_info *info; + int len = 0; + + list_for_each(lh, &if_list) { + info = list_entry(lh, struct ma_if_info, list); + len += sprintf(buffer + len, "%02d %010d %1d %1d\n", + info->interface_id, info->preference, + !!(info->status & MA_IFACE_HAS_ROUTER), + !!(info->status & MA_IFACE_CURRENT)); + } + + *start = buffer + offset; + + len -= offset; + + if (len > length) len = length; + + return len; + +} + +void ma_ctl_init(void) +{ + proc_net_create("mip6_iface", 0, if_proc_info); +} + +void ma_ctl_clean(void) +{ + proc_net_remove("mip6_iface"); +} diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/multiaccess_ctl.h linux-2.4.25/net/ipv6/mobile_ip6/multiaccess_ctl.h --- linux-2.4.25.old/net/ipv6/mobile_ip6/multiaccess_ctl.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.25/net/ipv6/mobile_ip6/multiaccess_ctl.h 2004-06-26 11:29:31.000000000 +0100 @@ -0,0 +1,77 @@ +/* + * 2001 (c) Oy L M Ericsson Ab + * + * Author: NomadicLab / Ericsson Research + * + * $Id$ + * + */ + +#ifndef _MULTIACCESS_CTL_H +#define _MULTIACCESS_CTL_H + +/* status */ +#define MA_IFACE_NOT_PRESENT 0x01 +#define MA_IFACE_NOT_USED 0x02 +#define MA_IFACE_HAS_ROUTER 0x04 +#define MA_IFACE_CURRENT 0x10 + +struct ma_if_uinfo { + int interface_id; + int preference; + __u8 status; +}; +/* + * @ma_ctl_get_preferred_id: returns most preferred interface id + */ +int ma_ctl_get_preferred_if(void); + +/* @ma_ctl_get_preference: returns preference for an interface + * @name: name of the interface (dev->name) + */ +int ma_ctl_get_preference(int ifi); + +/* + * Public function: ma_ctl_set_preference + * Description: Set preference of an existing interface (called by ioctl) + * Returns: + */ +void ma_ctl_set_preference(unsigned long); + +/* + * Public function: ma_ctl_add_iface + * Description: Inform control module to insert a new interface + * Returns: 0 if success, any other number means an error + */ +void ma_ctl_add_iface(int); + +/* + * Public function: ma_ctl_del_iface + * Description: Inform control module to remove an obsolete interface + * Returns: 0 if success, any other number means an error + */ +int ma_ctl_del_iface(int); + +/* + * Public function: ma_ctl_upd_iface + * Description: Inform control module of status change. + * Returns: 0 if success, any other number means an error + */ +int ma_ctl_upd_iface(int, int, int *); + +/* + * Public function: ma_ctl_init + * Description: XXX + * Returns: XXX + */ +void ma_ctl_init(void); + +/* + * Public function: ma_ctl_clean + * Description: XXX + * Returns: - + */ +void ma_ctl_clean(void); + + +#endif diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/ndisc_ha.c linux-2.4.25/net/ipv6/mobile_ip6/ndisc_ha.c --- linux-2.4.25.old/net/ipv6/mobile_ip6/ndisc_ha.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.25/net/ipv6/mobile_ip6/ndisc_ha.c 2004-06-26 11:29:31.000000000 +0100 @@ -0,0 +1,596 @@ +/* + * Mobile IPv6 Duplicate Address Detection Functions + * + * Authors: + * Krishna Kumar + * + * $Id$ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "debug.h" +#include "bcache.h" +#include "ha.h" /* mipv6_generate_ll_addr */ + +/* + * Binding Updates from MN are cached in this structure till DAD is performed. + * This structure is used to retrieve a pending Binding Update for the HA to + * reply to after performing DAD. The first cell is different from the rest as + * follows : + * 1. The first cell is used to chain the remaining cells. + * 2. The timeout of the first cell is used to delete expired entries + * in the list of cells, while the timeout of the other cells are + * used for timing out a NS request so as to reply to a BU. + * 3. The only elements of the first cell that are used are : + * next, prev, and callback_timer. + * + * TODO : Don't we need to do pneigh_lookup on the Link Local address ? + */ +struct mipv6_dad_cell { + /* Information needed for DAD management */ + struct mipv6_dad_cell *next; /* Next element on the DAD list */ + struct mipv6_dad_cell *prev; /* Prev element on the DAD list */ + __u16 probes; /* Number of times to probe for addr */ + __u16 flags; /* Entry flags - see below */ + struct timer_list callback_timer; /* timeout for entry */ + + /* Information needed for performing DAD */ + struct inet6_ifaddr *ifp; + int ifindex; + struct in6_addr daddr; + struct in6_addr haddr; /* home address */ + struct in6_addr ll_haddr; /* Link Local value of haddr */ + struct in6_addr coa; + struct in6_addr rep_coa; + __u32 ba_lifetime; + __u16 sequence; + __u8 bu_flags; +}; + +/* Values for the 'flags' field in the mipv6_dad_cell */ +#define DAD_INIT_ENTRY 0 +#define DAD_DUPLICATE_ADDRESS 1 +#define DAD_UNIQUE_ADDRESS 2 + +/* Head of the pending DAD list */ +static struct mipv6_dad_cell dad_cell_head; + +/* Lock to access the pending DAD list */ +static rwlock_t dad_lock = RW_LOCK_UNLOCKED; + +/* Timer routine which deletes 'expired' entries in the DAD list */ +static void mipv6_dad_delete_old_entries(unsigned long unused) +{ + struct mipv6_dad_cell *curr, *next; + unsigned long next_time = 0; + + write_lock(&dad_lock); + curr = dad_cell_head.next; + while (curr != &dad_cell_head) { + next = curr->next; + if (curr->flags != DAD_INIT_ENTRY) { + if (curr->callback_timer.expires <= jiffies) { + /* Entry has expired, free it up. */ + curr->next->prev = curr->prev; + curr->prev->next = curr->next; + in6_ifa_put(curr->ifp); + kfree(curr); + } else if (next_time < + curr->callback_timer.expires) { + next_time = curr->callback_timer.expires; + } + } + curr = next; + } + write_unlock(&dad_lock); + if (next_time) { + /* + * Start another timer if more cells need to be removed at + * a later stage. + */ + dad_cell_head.callback_timer.expires = next_time; + add_timer(&dad_cell_head.callback_timer); + } +} + +/* + * Queue a timeout routine to clean up 'expired' DAD entries. + */ +static void mipv6_start_dad_head_timer(struct mipv6_dad_cell *cell) +{ + unsigned long expire = jiffies + + cell->ifp->idev->nd_parms->retrans_time * 10; + + if (!timer_pending(&dad_cell_head.callback_timer) || + expire < dad_cell_head.callback_timer.expires) { + /* + * Add timer if none pending, or mod the timer if new + * cell needs to be expired before existing timer runs. + * + * We let the cell remain as long as possible, so that + * new BU's as part of retransmissions don't have to go + * through DAD before replying. + */ + dad_cell_head.callback_timer.expires = expire; + + /* + * Keep the cell around for atleast some time to handle + * retransmissions or BU's due to fast MN movement. This + * is needed otherwise a previous timeout can delete all + * expired entries including this new one. + */ + cell->callback_timer.expires = jiffies + + cell->ifp->idev->nd_parms->retrans_time * 5; + if (!timer_pending(&dad_cell_head.callback_timer)) { + add_timer(&dad_cell_head.callback_timer); + } else { + mod_timer(&dad_cell_head.callback_timer, expire); + } + } +} + + +/* Join solicited node MC address */ +static inline void mipv6_join_sol_mc_addr(struct in6_addr *addr, + struct net_device *dev) +{ + struct in6_addr maddr; + + /* Join solicited node MC address */ + addrconf_addr_solict_mult(addr, &maddr); + ipv6_dev_mc_inc(dev, &maddr); +} + +/* Leave solicited node MC address */ +static inline void mipv6_leave_sol_mc_addr(struct in6_addr *addr, + struct net_device *dev) +{ + struct in6_addr maddr; + + addrconf_addr_solict_mult(addr, &maddr); + ipv6_dev_mc_dec(dev, &maddr); +} + +/* Send a NS */ +static inline void mipv6_dad_send_ns(struct inet6_ifaddr *ifp, + struct in6_addr *haddr) +{ + struct in6_addr unspec; + struct in6_addr mcaddr; + + ipv6_addr_set(&unspec, 0, 0, 0, 0); + addrconf_addr_solict_mult(haddr, &mcaddr); + + /* addr is 'unspec' since we treat this address as transient */ + ndisc_send_ns(ifp->idev->dev, NULL, haddr, &mcaddr, &unspec); +} + +/* + * Search for a home address in the list of pending DAD's. Called from + * Neighbor Advertisement + * Return values : + * -1 : No DAD entry found for this advertisement, or entry already + * finished processing. + * 0 : Entry found waiting for DAD to finish. + */ +static int dad_search_haddr(struct in6_addr *ll_haddr, + struct in6_addr *daddr, struct in6_addr *haddr, + struct in6_addr *coa, struct in6_addr *rep_coa, + __u16 * seq, struct inet6_ifaddr **ifp) +{ + struct mipv6_dad_cell *cell; + + read_lock(&dad_lock); + cell = dad_cell_head.next; + while (cell != &dad_cell_head && + ipv6_addr_cmp(&cell->ll_haddr, ll_haddr) && + ipv6_addr_cmp(&cell->haddr, ll_haddr)) { + cell = cell->next; + } + if (cell == &dad_cell_head || cell->flags != DAD_INIT_ENTRY) { + /* Not found element, or element already finished processing */ + if (cell != &dad_cell_head) { + /* + * Set the state to DUPLICATE, even if it was UNIQUE + * earlier. It is not needed to setup timer via + * mipv6_start_dad_head_timer since this must have + * already been done. + */ + cell->flags = DAD_DUPLICATE_ADDRESS; + } + read_unlock(&dad_lock); + return -1; + } + + /* + * The NA found an unprocessed entry in the DAD list. Expire this + * entry since another node advertised this address. Caller should + * reject BU (DAD failed). + */ + ipv6_addr_copy(daddr, &cell->daddr); + ipv6_addr_copy(haddr, &cell->haddr); + ipv6_addr_copy(coa, &cell->coa); + ipv6_addr_copy(rep_coa, &cell->rep_coa); + *seq = cell->sequence; + *ifp = cell->ifp; + + if (del_timer(&cell->callback_timer) == 0) { + /* Timer already deleted, race with Timeout Handler */ + /* No action needed */ + } + + cell->flags = DAD_DUPLICATE_ADDRESS; + + /* Now leave this address to avoid future processing of NA's */ + mipv6_leave_sol_mc_addr(&cell->ll_haddr, cell->ifp->idev->dev); + /* Leave also global address, if link local address was in use */ + if (ipv6_addr_cmp(&cell->ll_haddr, &cell->haddr)) + mipv6_leave_sol_mc_addr(&cell->haddr, cell->ifp->idev->dev); + /* Start dad_head timer to remove this entry */ + mipv6_start_dad_head_timer(cell); + + read_unlock(&dad_lock); + + return 0; +} + +/* ENTRY routine called via Neighbor Advertisement */ +void mipv6_check_dad(struct in6_addr *ll_haddr) +{ + struct in6_addr daddr, haddr, coa, rep_coa; + struct inet6_ifaddr *ifp; + __u16 seq; + + if (dad_search_haddr(ll_haddr, &daddr, &haddr, &coa, &rep_coa, &seq, + &ifp) < 0) { + /* + * Didn't find entry, or no action needed (the action has + * already been performed). + */ + return; + } + + /* + * A DAD cell was present, meaning that there is a pending BU + * request for 'haddr' - reject the BU. + */ + mipv6_bu_finish(ifp, 0, DUPLICATE_ADDR_DETECT_FAIL, + &daddr, &haddr, &coa, &rep_coa, 0, seq, 0, NULL); + return; +} + +/* + * Check if the passed 'cell' is in the list of pending DAD's. Called from + * the Timeout Handler. + * + * Assumes that the caller is holding the dad_lock in reader mode. + */ +static int dad_search_cell(struct mipv6_dad_cell *cell) +{ + struct mipv6_dad_cell *tmp; + + tmp = dad_cell_head.next; + while (tmp != &dad_cell_head && tmp != cell) { + tmp = tmp->next; + } + if (tmp == cell) { + if (cell->flags == DAD_INIT_ENTRY) { + /* Found valid entry */ + if (--cell->probes == 0) { + /* + * Retransmission's are over - return success. + */ + cell->flags = DAD_UNIQUE_ADDRESS; + + /* + * Leave this address to avoid future + * processing of NA's. + */ + mipv6_leave_sol_mc_addr(&cell->ll_haddr, + cell->ifp->idev-> + dev); + if (ipv6_addr_cmp(&cell->ll_haddr, &cell->haddr)) + mipv6_leave_sol_mc_addr(&cell->haddr, + cell->ifp->idev->dev); + /* start timeout to delete this cell. */ + mipv6_start_dad_head_timer(cell); + return 0; + } + /* + * Retransmission not finished, send another NS and + * return failure. + */ + mipv6_dad_send_ns(cell->ifp, &cell->ll_haddr); + if (ipv6_addr_cmp(&cell->ll_haddr, &cell->haddr)) + mipv6_leave_sol_mc_addr(&cell->haddr, + cell->ifp->idev->dev); + cell->callback_timer.expires = jiffies + + cell->ifp->idev->nd_parms->retrans_time; + add_timer(&cell->callback_timer); + } else { + /* + * This means that an NA was received before the + * timeout and when the state changed from + * DAD_INIT_ENTRY, the BU got failed as a result. + * There is nothing to be done. + */ + } + } + return -1; +} + +/* ENTRY routine called via Timeout */ +static void mipv6_dad_timeout(unsigned long arg) +{ + __u8 ba_status = SUCCESS; + struct in6_addr daddr; + struct in6_addr haddr; + struct in6_addr coa; + struct in6_addr rep_coa; + struct inet6_ifaddr *ifp; + int ifindex; + __u32 ba_lifetime; + __u16 sequence; + __u8 flags; + struct mipv6_dad_cell *cell = (struct mipv6_dad_cell *) arg; + + /* + * If entry is not in the list, we have already sent BU Failure + * after getting a NA. + */ + read_lock(&dad_lock); + if (dad_search_cell(cell) < 0) { + /* + * 'cell' is no longer valid (may not be in the list or + * is already processed, due to NA processing), or NS + * retransmissions are not yet over. + */ + read_unlock(&dad_lock); + return; + } + + /* This is the final Timeout. Send Bind Ack Success */ + + ifp = cell->ifp; + ifindex = cell->ifindex; + ba_lifetime = cell->ba_lifetime; + sequence = cell->sequence; + flags = cell->bu_flags; + + ipv6_addr_copy(&daddr, &cell->daddr); + ipv6_addr_copy(&haddr, &cell->haddr); + ipv6_addr_copy(&coa, &cell->coa); + ipv6_addr_copy(&rep_coa, &cell->rep_coa); + read_unlock(&dad_lock); + + /* Send BU Acknowledgement Success */ + mipv6_bu_finish(ifp, ifindex, ba_status, + &daddr, &haddr, &coa, &rep_coa, + ba_lifetime, sequence, flags, NULL); + return; +} + +/* + * Check if original home address exists in our DAD pending list, if so return + * the cell. + * + * Assumes that the caller is holding the dad_lock in writer mode. + */ +static struct mipv6_dad_cell *mipv6_dad_get_cell(struct in6_addr *haddr) +{ + struct mipv6_dad_cell *cell; + + cell = dad_cell_head.next; + while (cell != &dad_cell_head + && ipv6_addr_cmp(&cell->haddr, haddr)) { + cell = cell->next; + } + if (cell == &dad_cell_head) { + /* Not found element */ + return NULL; + } + return cell; +} + +/* + * Save all parameters needed for doing a Bind Ack in the mipv6_dad_cell + * structure. + */ +static void mipv6_dad_save_cell(struct mipv6_dad_cell *cell, + struct inet6_ifaddr *ifp, int ifindex, + struct in6_addr *daddr, + struct in6_addr *haddr, + struct in6_addr *coa, + struct in6_addr *rep_coa, + __u32 ba_lifetime, + __u16 sequence, __u8 flags) +{ + in6_ifa_hold(ifp); + cell->ifp = ifp; + cell->ifindex = ifindex; + + ipv6_addr_copy(&cell->daddr, daddr); + ipv6_addr_copy(&cell->haddr, haddr); + ipv6_addr_copy(&cell->coa, coa); + ipv6_addr_copy(&cell->rep_coa, rep_coa); + + /* Convert cell->ll_haddr to Link Local address */ + if (flags & MIPV6_BU_F_LLADDR) + mipv6_generate_ll_addr(&cell->ll_haddr, haddr); + else + ipv6_addr_copy(&cell->ll_haddr, haddr); + + cell->ba_lifetime = ba_lifetime; + cell->sequence = sequence; + cell->bu_flags = flags; +} + +/* + * Top level DAD routine for performing DAD. + * + * Return values + * 0 : Don't need to do DAD. + * 1 : Need to do DAD. + * -n : Error, where 'n' is the reason for the error. + * + * Assumption : DAD process has been optimized by using cached values upto + * some time. However sometimes this can cause problems. Eg. when the first + * BU was received, DAD might have failed. Before the second BU arrived, + * the node using MN's home address might have stopped using it, but still + * we will return DAD_DUPLICATE_ADDRESS based on the first DAD's result. Or + * this can go the other way around. However, it is a very small possibility + * and thus optimization is turned on by default. It is possible to change + * this feature (needs a little code-rewriting in this routine), but + * currently DAD result is being cached for performance reasons. + */ +int mipv6_dad_start(struct inet6_ifaddr *ifp, int ifindex, + struct in6_addr *daddr, struct in6_addr *haddr, + struct in6_addr *coa, struct in6_addr *rep_coa, + __u32 ba_lifetime, __u16 sequence, __u8 flags) +{ + int found; + struct mipv6_dad_cell *cell; + struct mipv6_bce bc_entry; + + if (ifp->idev->cnf.dad_transmits == 0) { + /* DAD is not configured on the HA, return SUCCESS */ + return 0; + } + + if (mipv6_bcache_get(haddr, daddr, &bc_entry) == 0) { + /* + * We already have an entry in our cache - don't need to + * do DAD as we are already defending this home address. + */ + return 0; + } + + write_lock(&dad_lock); + if ((cell = mipv6_dad_get_cell(haddr)) != NULL) { + /* + * An existing entry for BU was found in our cache due + * to retransmission of the BU or a new COA registration. + */ + switch (cell->flags) { + case DAD_INIT_ENTRY: + /* Old entry is waiting for DAD to complete */ + break; + case DAD_UNIQUE_ADDRESS: + /* DAD is finished successfully - return success. */ + write_unlock(&dad_lock); + return 0; + case DAD_DUPLICATE_ADDRESS: + /* + * DAD is finished and we got a NA while doing BU - + * return failure. + */ + write_unlock(&dad_lock); + return -DUPLICATE_ADDR_DETECT_FAIL; + default: + /* Unknown state - should never happen */ + DEBUG(DBG_WARNING, + "cell entry in unknown state : %d", + cell->flags); + write_unlock(&dad_lock); + return -REASON_UNSPECIFIED; + } + found = 1; + } else { + if ((cell = (struct mipv6_dad_cell *) + kmalloc(sizeof(struct mipv6_dad_cell), GFP_ATOMIC)) + == NULL) { + return -INSUFFICIENT_RESOURCES; + } + found = 0; + } + + mipv6_dad_save_cell(cell, ifp, ifindex, daddr, haddr, coa, rep_coa, + ba_lifetime, sequence, flags); + + if (!found) { + cell->flags = DAD_INIT_ENTRY; + cell->probes = ifp->idev->cnf.dad_transmits; + + /* Insert element on dad_cell_head list */ + dad_cell_head.prev->next = cell; + cell->next = &dad_cell_head; + cell->prev = dad_cell_head.prev; + dad_cell_head.prev = cell; + write_unlock(&dad_lock); + if (flags & MIPV6_BU_F_LLADDR) { + /* join the solicited node MC of the global homeaddr.*/ + mipv6_join_sol_mc_addr(&cell->haddr, ifp->idev->dev); + /* Send a NS */ + mipv6_dad_send_ns(ifp, &cell->haddr); + } + /* join the solicited node MC of the homeaddr. */ + mipv6_join_sol_mc_addr(&cell->ll_haddr, ifp->idev->dev); + + /* Send a NS */ + mipv6_dad_send_ns(ifp, &cell->ll_haddr); + + /* Initialize timer for this cell to timeout the NS. */ + init_timer(&cell->callback_timer); + cell->callback_timer.data = (unsigned long) cell; + cell->callback_timer.function = mipv6_dad_timeout; + cell->callback_timer.expires = jiffies + + ifp->idev->nd_parms->retrans_time; + add_timer(&cell->callback_timer); + } else { + write_unlock(&dad_lock); + } + return 1; +} + +void __init mipv6_dad_init(void) +{ + dad_cell_head.next = dad_cell_head.prev = &dad_cell_head; + init_timer(&dad_cell_head.callback_timer); + dad_cell_head.callback_timer.data = 0; + dad_cell_head.callback_timer.function = + mipv6_dad_delete_old_entries; +} + +void __exit mipv6_dad_exit(void) +{ + struct mipv6_dad_cell *curr, *next; + + write_lock_bh(&dad_lock); + del_timer(&dad_cell_head.callback_timer); + + curr = dad_cell_head.next; + while (curr != &dad_cell_head) { + next = curr->next; + del_timer(&curr->callback_timer); + if (curr->flags == DAD_INIT_ENTRY) { + /* + * We were in DAD_INIT state and listening to the + * solicited node MC address - need to stop that. + */ + mipv6_leave_sol_mc_addr(&curr->ll_haddr, + curr->ifp->idev->dev); + if (ipv6_addr_cmp(&curr->ll_haddr, &curr->haddr)) + mipv6_leave_sol_mc_addr(&curr->haddr, + curr->ifp->idev->dev); + } + in6_ifa_put(curr->ifp); + kfree(curr); + curr = next; + } + dad_cell_head.next = dad_cell_head.prev = &dad_cell_head; + write_unlock_bh(&dad_lock); +} diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/prefix.c linux-2.4.25/net/ipv6/mobile_ip6/prefix.c --- linux-2.4.25.old/net/ipv6/mobile_ip6/prefix.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.25/net/ipv6/mobile_ip6/prefix.c 2004-06-26 11:29:31.000000000 +0100 @@ -0,0 +1,217 @@ +/** + * Prefix solicitation and advertisement + * + * Authors: + * Jaakko Laine + * + * $Id$ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mipv6_icmp.h" +#include "debug.h" +#include "sortedlist.h" +#include "prefix.h" +#include "config.h" + +#define INFINITY 0xffffffff + +struct timer_list pfx_timer; + +struct list_head pfx_list; +rwlock_t pfx_list_lock = RW_LOCK_UNLOCKED; + +int compare_pfx_list_entry(const void *data1, const void *data2, + int datalen) +{ + struct pfx_list_entry *e1 = (struct pfx_list_entry *) data1; + struct pfx_list_entry *e2 = (struct pfx_list_entry *) data2; + + return ((ipv6_addr_cmp(&e1->daddr, &e2->daddr) == 0) + && (e2->ifindex == -1 || e1->ifindex == e2->ifindex)); +} + +/** + * mipv6_pfx_cancel_send - cancel pending items to daddr from saddr + * @daddr: Destination address + * @ifindex: pending items on this interface will be canceled + * + * if ifindex == -1, all items to daddr will be removed + */ +void mipv6_pfx_cancel_send(struct in6_addr *daddr, int ifindex) +{ + unsigned long tmp; + struct pfx_list_entry entry; + + DEBUG_FUNC(); + + /* We'll just be comparing these parts... */ + memcpy(&entry.daddr, daddr, sizeof(struct in6_addr)); + entry.ifindex = ifindex; + + write_lock_bh(&pfx_list_lock); + + while (mipv6_slist_del_item(&pfx_list, &entry, + compare_pfx_list_entry) == 0) + ; + + if ((tmp = mipv6_slist_get_first_key(&pfx_list))) + mod_timer(&pfx_timer, tmp); + + write_unlock_bh(&pfx_list_lock); +} + +/** + * mipv6_pfx_add_ha - add a new HA to send prefix solicitations to + * @daddr: address of HA + * @saddr: our address to use as source address + * @ifindex: interface index + */ +void mipv6_pfx_add_ha(struct in6_addr *daddr, struct in6_addr *saddr, + int ifindex) +{ + unsigned long tmp; + struct pfx_list_entry entry; + + DEBUG_FUNC(); + + memcpy(&entry.daddr, daddr, sizeof(struct in6_addr)); + memcpy(&entry.saddr, saddr, sizeof(struct in6_addr)); + entry.retries = 0; + entry.ifindex = ifindex; + + write_lock_bh(&pfx_list_lock); + if (mipv6_slist_modify(&pfx_list, &entry, sizeof(struct pfx_list_entry), + jiffies + INITIAL_SOLICIT_TIMER * HZ, + compare_pfx_list_entry)) + DEBUG(DBG_WARNING, "Cannot add new HA to pfx list"); + + if ((tmp = mipv6_slist_get_first_key(&pfx_list))) + mod_timer(&pfx_timer, tmp); + write_unlock_bh(&pfx_list_lock); +} + +int mipv6_pfx_add_home(int ifindex, struct in6_addr *saddr, + struct in6_addr *daddr, unsigned long min_expire) +{ + unsigned long tmp; + + write_lock(&pfx_list_lock); + + if (min_expire != INFINITY) { + unsigned long expire; + struct pfx_list_entry entry; + + memcpy(&entry.daddr, saddr, sizeof(struct in6_addr)); + memcpy(&entry.saddr, daddr, sizeof(struct in6_addr)); + entry.retries = 0; + entry.ifindex = ifindex; + + /* This is against the RFC 3775, but we need to set + * a minimum interval for a prefix solicitation. + * Otherwise a prefix solicitation storm will + * result if valid lifetime of the prefix is + * smaller than MAX_PFX_ADV_DELAY + */ + min_expire -= MAX_PFX_ADV_DELAY; + min_expire = min_expire < MIN_PFX_SOL_DELAY ? MIN_PFX_SOL_DELAY : min_expire; + + expire = jiffies + min_expire * HZ; + + if (mipv6_slist_modify(&pfx_list, &entry, + sizeof(struct pfx_list_entry), + expire, + compare_pfx_list_entry) != 0) + DEBUG(DBG_WARNING, "Cannot add new entry to pfx_list"); + } + + if ((tmp = mipv6_slist_get_first_key(&pfx_list))) + mod_timer(&pfx_timer, tmp); + + write_unlock(&pfx_list_lock); + + return 0; +} + +/** + * set_ha_pfx_list - manipulate pfx_list for HA when timer goes off + * @entry: pfx_list_entry that is due + */ +static void set_ha_pfx_list(struct pfx_list_entry *entry) +{ +} + +/** + * set_mn_pfx_list - manipulate pfx_list for MN when timer goes off + * @entry: pfx_list_entry that is due + */ +static void set_mn_pfx_list(struct pfx_list_entry *entry) +{ +} + +/** + * pfx_timer_handler - general timer handler + * @dummy: dummy + * + * calls set_ha_pfx_list and set_mn_pfx_list to do the thing when + * a timer goes off + */ +static void pfx_timer_handler(unsigned long dummy) +{ + unsigned long tmp; + struct pfx_list_entry *entry; + + DEBUG_FUNC(); + + write_lock(&pfx_list_lock); + if (!(entry = mipv6_slist_get_first(&pfx_list))) + goto out; + + if (mip6node_cnf.capabilities & CAP_HA) + set_ha_pfx_list(entry); + if (mip6node_cnf.capabilities & CAP_MN) + set_mn_pfx_list(entry); + if ((tmp = mipv6_slist_get_first_key(&pfx_list))) + mod_timer(&pfx_timer, tmp); + + out: + write_unlock(&pfx_list_lock); +} + +int mipv6_initialize_pfx_icmpv6(void) +{ + INIT_LIST_HEAD(&pfx_list); + + init_timer(&pfx_timer); + pfx_timer.function = pfx_timer_handler; + + return 0; +} + +void mipv6_shutdown_pfx_icmpv6(void) +{ + struct prefix_info *tmp; + + if (timer_pending(&pfx_timer)) + del_timer(&pfx_timer); + + write_lock_bh(&pfx_list_lock); + while ((tmp = mipv6_slist_del_first(&pfx_list))) + kfree(tmp); + write_unlock_bh(&pfx_list_lock); +} diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/prefix.h linux-2.4.25/net/ipv6/mobile_ip6/prefix.h --- linux-2.4.25.old/net/ipv6/mobile_ip6/prefix.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.25/net/ipv6/mobile_ip6/prefix.h 2004-06-26 11:29:31.000000000 +0100 @@ -0,0 +1,57 @@ +/* + * MIPL Mobile IPv6 Prefix solicitation and advertisement + * + * $Id$ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _PREFIX_H +#define _PREFIX_H + +#include + +struct pfx_list_entry { + struct in6_addr daddr; + struct in6_addr saddr; + int retries; + int ifindex; +}; + +extern struct list_head pfx_list; +extern rwlock_t pfx_list_lock; +extern struct timer_list pfx_timer; + +int compare_pfx_list_entry(const void *data1, const void *data2, + int datalen); + +/** + * mipv6_pfx_cancel_send - cancel pending pfx_advs/sols to daddr + * @daddr: destination address + * @ifindex: pending items on this interface will be canceled + * + * if ifindex == -1, all items to daddr will be removed + */ +void mipv6_pfx_cancel_send(struct in6_addr *daddr, int ifindex); + +/** + * mipv6_pfx_add_ha - add a new HA to send prefix solicitations to + * @daddr: address of HA + * @saddr: our address to use as source address + * @ifindex: interface index + */ +void mipv6_pfx_add_ha(struct in6_addr *daddr, struct in6_addr *saddr, + int ifindex); + +void mipv6_pfxs_modified(struct prefix_info *pinfo, int ifindex); + +int mipv6_pfx_add_home(int ifindex, struct in6_addr *daddr, + struct in6_addr *saddr, unsigned long min_expire); + +int mipv6_initialize_pfx_icmpv6(void); +void mipv6_shutdown_pfx_icmpv6(void); + +#endif diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/prefix_ha.c linux-2.4.25/net/ipv6/mobile_ip6/prefix_ha.c --- linux-2.4.25.old/net/ipv6/mobile_ip6/prefix_ha.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.25/net/ipv6/mobile_ip6/prefix_ha.c 2004-06-26 11:29:31.000000000 +0100 @@ -0,0 +1,122 @@ +/** + * Prefix advertisement for Home Agent + * + * Authors: + * Jaakko Laine + * + * $Id$ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mipv6_icmp.h" +#include "debug.h" +#include "sortedlist.h" +#include "util.h" +#include "bcache.h" +#include "config.h" +#include "prefix.h" + +/** + * pfx_adv_iterator - modify pfx_list entries according to new prefix info + * @data: MN's home registration bcache_entry + * @args: new prefix info + * @sortkey: ignored + */ +static int pfx_adv_iterator(void *data, void *args, unsigned long sortkey) +{ + struct mipv6_bce *bc_entry = (struct mipv6_bce *) data; + struct prefix_info *pinfo = (struct prefix_info *) args; + + if (mipv6_prefix_compare(&bc_entry->coa, &pinfo->prefix, + pinfo->prefix_len) == 0) { + struct pfx_list_entry pfx_entry; + + memcpy(&pfx_entry.daddr, &bc_entry->coa, + sizeof(struct in6_addr)); + memcpy(&pfx_entry.daddr, &bc_entry->our_addr, + sizeof(struct in6_addr)); + pfx_entry.retries = 0; + pfx_entry.ifindex = bc_entry->ifindex; + + mipv6_slist_modify(&pfx_list, &pfx_entry, + sizeof(struct pfx_list_entry), + jiffies + + net_random() % (MAX_PFX_ADV_DELAY * HZ), + compare_pfx_list_entry); + } + + return 0; +} + +struct homereg_iterator_args { + struct list_head *head; + int count; +}; + +static int homereg_iterator(void *data, void *args, unsigned long *sortkey) +{ + struct mipv6_bce *entry = (struct mipv6_bce *) data; + struct homereg_iterator_args *state = + (struct homereg_iterator_args *) args; + + if (entry->type == HOME_REGISTRATION) { + mipv6_slist_add(state->head, entry, + sizeof(struct mipv6_bce), + state->count); + state->count++; + } + return 0; +} + +static int mipv6_bcache_get_homeregs(struct list_head *head) +{ + struct homereg_iterator_args args; + + DEBUG_FUNC(); + + args.count = 0; + args.head = head; + + mipv6_bcache_iterate(homereg_iterator, &args); + return args.count; +} + +/** + * mipv6_prefix_added - prefix was added to interface, act accordingly + * @pinfo: prefix_info that was added + * @ifindex: interface index + */ +void mipv6_pfxs_modified(struct prefix_info *pinfo, int ifindex) +{ + int count; + unsigned long tmp; + struct list_head home_regs; + + DEBUG_FUNC(); + + INIT_LIST_HEAD(&home_regs); + + if (!(count = mipv6_bcache_get_homeregs(&home_regs))) + return; + + write_lock_bh(&pfx_list_lock); + mipv6_slist_for_each(&home_regs, pinfo, pfx_adv_iterator); + if ((tmp = mipv6_slist_get_first_key(&pfx_list))) + mod_timer(&pfx_timer, tmp); + write_unlock_bh(&pfx_list_lock); +} diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/rr_crypto.c linux-2.4.25/net/ipv6/mobile_ip6/rr_crypto.c --- linux-2.4.25.old/net/ipv6/mobile_ip6/rr_crypto.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.25/net/ipv6/mobile_ip6/rr_crypto.c 2004-06-26 11:29:31.000000000 +0100 @@ -0,0 +1,255 @@ +/* + * rr_cookie.c - Mobile IPv6 return routability crypto + * Author : Henrik Petander + * + * $Id$ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "debug.h" +#include "hmac.h" +#include "rr_crypto.h" + +#define DBG_RR 5 + +u8 k_CN[HMAC_SHA1_KEY_SIZE]; // secret key of CN + +u16 curr_index = 0; + +struct nonce_timestamp nonce_table[MAX_NONCES]; +spinlock_t nonce_lock = SPIN_LOCK_UNLOCKED; +void update_nonces(void); + +/** nonce_is_fresh - whether the nonce was generated recently + * + * @non_ts : table entry containing the nonce and a timestamp + * @interval : if nonce was generated within interval seconds it is fresh + * + * Returns 1 if the nonce is fresh, 0 otherwise. + */ +static int nonce_is_fresh(struct nonce_timestamp *non_ts, unsigned long interval) +{ + if (time_before(jiffies, non_ts->timestamp + interval * HZ) && !non_ts->invalid) + return 1; + return 0; +} +void mipv6_rr_invalidate_nonce(u16 nonce_ind) +{ + spin_lock_bh(&nonce_lock); + if (nonce_ind > MAX_NONCES) { + spin_unlock_bh(&nonce_lock); + return; + } + nonce_table[nonce_ind].invalid = 1; + spin_unlock_bh(&nonce_lock); +} +/* Returns a pointer to a new nonce */ +struct mipv6_rr_nonce * mipv6_rr_get_new_nonce(void) +{ + struct mipv6_rr_nonce *nce = kmalloc(sizeof(*nce), GFP_ATOMIC); + + if (!nce) + return NULL; + // Lock nonces here + spin_lock_bh(&nonce_lock); + // If nonce is not fresh create new one + if (!nonce_is_fresh(&nonce_table[curr_index], MIPV6_RR_NONCE_LIFETIME)) { + // increment the last nonce pointer and create new nonce + curr_index++; + // Wrap around + if (curr_index == MAX_NONCES) + curr_index = 0; + // Get random data to fill the nonce data + get_random_bytes(nonce_table[curr_index].nonce.data, MIPV6_RR_NONCE_DATA_LENGTH); + // Fill the index field + nonce_table[curr_index].nonce.index = curr_index; + nonce_table[curr_index].invalid = 0; + nonce_table[curr_index].timestamp = jiffies; + } + spin_unlock_bh(&nonce_lock); + memcpy(nce, &nonce_table[curr_index].nonce, sizeof(*nce)); + // Unlock nonces + return nce; +} +/** mipv6_rr_nonce_get_by_index - returns a nonce for index + * @nonce_ind : index of the nonce + * + * Returns a nonce or NULL if the nonce index was invalid or the nonce + * for the index was not fresh. + */ +struct mipv6_rr_nonce * mipv6_rr_nonce_get_by_index(u16 nonce_ind) +{ + struct mipv6_rr_nonce *nce = NULL; + + spin_lock_bh(&nonce_lock); + if (nonce_ind >= MAX_NONCES) { + DEBUG(DBG_WARNING, "Nonce index field from BU invalid"); + + /* Here a double of the nonce_lifetime is used for freshness + * verification, since the nonces + * are not created in response to every initiator packet + */ + } else if (nonce_is_fresh(&nonce_table[nonce_ind], 2 * MIPV6_RR_NONCE_LIFETIME)) { + nce = kmalloc(sizeof(*nce), GFP_ATOMIC); + memcpy(nce, &nonce_table[nonce_ind].nonce, sizeof(*nce)); + } + spin_unlock_bh(&nonce_lock); + + return nce; +} + +/* Fills rr test init cookies with random bytes */ +void mipv6_rr_mn_cookie_create(u8 *cookie) +{ + get_random_bytes(cookie, MIPV6_RR_COOKIE_LENGTH); +} + +/** mipv6_rr_cookie_create - builds a home or care-of cookie + * + * @addr : the home or care-of address from HoTI or CoTI + * @ckie : memory where the cookie is copied to + * @nce : pointer to a nonce used for the calculation, nce is freed during the function + * + */ +int mipv6_rr_cookie_create(struct in6_addr *addr, u8 **ckie, + u16 nonce_index) +{ + struct ah_processing ah_proc; + u8 digest[HMAC_SHA1_HASH_LEN]; + struct mipv6_rr_nonce *nce; + + if ((nce = mipv6_rr_nonce_get_by_index(nonce_index))== NULL) + return -1; + + if (*ckie == NULL && (*ckie = kmalloc(MIPV6_RR_COOKIE_LENGTH, + GFP_ATOMIC)) == NULL) { + kfree(nce); + return -1; + } + /* Calculate the full hmac-sha1 digest from address and nonce using the secret key of cn */ + + if (ah_hmac_sha1_init(&ah_proc, k_CN, HMAC_SHA1_KEY_SIZE) < 0) { + DEBUG(DBG_ERROR, "Hmac sha1 initialization failed"); + kfree(nce); + return -1; + } + + ah_hmac_sha1_loop(&ah_proc, addr, sizeof(*addr)); + ah_hmac_sha1_loop(&ah_proc, nce->data, MIPV6_RR_NONCE_DATA_LENGTH); + ah_hmac_sha1_result(&ah_proc, digest); + + + /* clean up nonce */ + kfree(nce); + + /* Copy first 64 bits of hash target to the cookie */ + memcpy(*ckie, digest, MIPV6_RR_COOKIE_LENGTH); + return 0; +} + +/** mipv6_rr_key_calc - creates BU authentication key + * + * @hoc : Home Cookie + * @coc : Care-of Cookie + * + * Returns BU authentication key of length HMAC_SHA1_KEY_SIZE or NULL in error cases, + * caller needs to free the key. + */ +u8 *mipv6_rr_key_calc(u8 *hoc, u8 *coc) +{ + + u8 *key_bu = kmalloc(HMAC_SHA1_KEY_SIZE, GFP_ATOMIC); + SHA1_CTX c; + + if (!key_bu) { + DEBUG(DBG_CRITICAL, "Memory allocation failed, could nort create BU authentication key"); + return NULL; + } + + /* Calculate the key from home and care-of cookies + * Kbu = sha1(home_cookie | care-of cookie) + * or KBu = sha1(home_cookie), if MN deregisters + */ + sha1_init(&c); + sha1_compute(&c, hoc, MIPV6_RR_COOKIE_LENGTH); + if (coc) + sha1_compute(&c, coc, MIPV6_RR_COOKIE_LENGTH); + sha1_final(&c, key_bu); + DEBUG(DBG_RR, "Home and Care-of cookies used for calculating key "); + debug_print_buffer(DBG_RR, hoc, MIPV6_RR_COOKIE_LENGTH); + if (coc) + debug_print_buffer(DBG_RR, coc, MIPV6_RR_COOKIE_LENGTH); + + return key_bu; +} + +void mipv6_rr_init(void) +{ + get_random_bytes(k_CN, HMAC_SHA1_KEY_SIZE); + memset(nonce_table, 0, MAX_NONCES * sizeof(struct nonce_timestamp)); +} + +#ifdef TEST_MIPV6_RR_CRYPTO +void mipv6_test_rr(void) +{ + struct mipv6_rr_nonce *nonce; + struct in6_addr a1, a2; + int ind1, ind2; + u8 *ckie1 = NULL, *ckie2 = NULL; + u8 *key_mn = NULL, *key_cn = NULL; + mipv6_init_rr(); + + nonce = mipv6_rr_get_new_nonce(); + if (!nonce) { + printk("mipv6_rr_get_new_nonce() failed, at 1! \n"); + return; + } + mipv6_rr_cookie_create(&a1, &ckie1, nonce->index); + ind1 = nonce->index; + kfree(nonce); + + nonce = mipv6_rr_get_new_nonce(); + if (!nonce) { + printk("mipv6_rr_get_new_nonce() failed, at 2! \n"); + return; + } + + mipv6_rr_cookie_create(&a2, &ckie2, nonce->index); + ind2 = nonce->index; + key_mn = mipv6_rr_key_calc(ckie1, ckie2); + + /* Create home and coa cookies based on indices */ + mipv6_rr_cookie_create(&a1, &ckie1, ind1); + mipv6_rr_cookie_create(&a2, &ckie2, ind2); + key_cn = mipv6_rr_key_calc(ckie1, ckie2); + if (!key_cn || !key_mn) { + printk("creation of secret key failed!\n"); + return; + } + if(memcmp(key_cn, key_mn, HMAC_SHA1_KEY_SIZE)) + printk("mipv6_rr_key_calc produced different keys for MN and CN \n"); + else + printk("mipv6_rr_crypto test OK\n"); + kfree(nonce); + kfree(key_cn); + kfree(key_mn); +} +#endif diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/rr_crypto.h linux-2.4.25/net/ipv6/mobile_ip6/rr_crypto.h --- linux-2.4.25.old/net/ipv6/mobile_ip6/rr_crypto.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.25/net/ipv6/mobile_ip6/rr_crypto.h 2004-06-26 11:29:31.000000000 +0100 @@ -0,0 +1,72 @@ +/* + * MIPL Mobile IPv6 Return routability crypto prototypes + * + * $Id:$ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _RR_CRYPTO +#define _RR_CRYPTO + +#include + +/* Macros and data structures */ + +#define MIPV6_RR_NONCE_LIFETIME 60 +#define MIPV6_RR_NONCE_DATA_LENGTH 8 +#define MIPV6_RR_COOKIE_LENGTH 8 +#define COOKIE_SIZE 8 +#define MAX_NONCES 4 +#define HMAC_SHA1_KEY_SIZE 20 + +struct mipv6_rr_nonce { + u_int16_t index; + u_int8_t data[MIPV6_RR_NONCE_DATA_LENGTH]; +}; + +struct nonce_timestamp { + struct mipv6_rr_nonce nonce; + unsigned long timestamp; + u_int8_t invalid; +}; + +/* Function definitions */ + +/* Return 1 if equal, 0 if not */ +static __inline__ int mipv6_equal_cookies(u8 *c1, u8 *c2) +{ + return (memcmp(c1, c2, MIPV6_RR_COOKIE_LENGTH) == 0); +} + +/* Function declarations */ + +/* Create cookie for HoTi and CoTi */ +extern void mipv6_rr_mn_cookie_create(u8 *cookie); + +/* Create cookie for HoT and CoT */ +extern int mipv6_rr_cookie_create(struct in6_addr *addr, u8 **ckie, u16 nonce_index); + +/* Calculate return routability key from home and care-of cookies, key length is + * HMAC_SHA1_KEY_SIZE + */ +extern u_int8_t *mipv6_rr_key_calc(u8 *hoc, u8 *coc); + +extern struct mipv6_rr_nonce *mipv6_rr_get_new_nonce(void); + +/* For avoiding replay attacks when MN deregisters */ +extern void mipv6_rr_invalidate_nonce(u16 nonce_index); +/* + * initializes the return routability crypto + */ + +void mipv6_rr_init(void); + +#ifdef TEST_MIPV6_RR_CRYPTO +void mipv6_test_rr(void); +#endif /* TEST_MIPV6_RR_CRYPTO */ + +#endif /* RR_CRYPTO */ diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/sortedlist.c linux-2.4.25/net/ipv6/mobile_ip6/sortedlist.c --- linux-2.4.25.old/net/ipv6/mobile_ip6/sortedlist.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.25/net/ipv6/mobile_ip6/sortedlist.c 2004-06-26 11:29:31.000000000 +0100 @@ -0,0 +1,349 @@ +/** + * Sorted list - linked list with sortkey. + * + * Authors: + * Jaakko Laine + * + * $Id$ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include + +struct mipv6_sorted_list_entry { + struct list_head list; + void *data; + int datalen; + unsigned long sortkey; +}; + +/** + * compare - compares two arbitrary data items + * @data1: first data item + * @data2: second data item + * @datalen: length of data items in bits + * + * datalen is in bits! + */ +int mipv6_bitwise_compare(const void *data1, const void *data2, int datalen) +{ + int n = datalen; + __u8 * ptr1 = (__u8 *)data1; + __u8 * ptr2 = (__u8 *)data2; + + for (; n>=0; n-=8, ptr1++, ptr2++) { + if (n >= 8) { + if (*ptr1 != *ptr2) + return 0; + } else { + if ((*ptr1 ^ *ptr2) & ((~0) << (8 - n))) + return 0; + } + } + + return 1; +} + +/** + * mipv6_slist_add - add an entry to sorted list + * @head: list_head of the sorted list + * @data: item to store + * @datalen: length of data (in bytes) + * @key: sortkey of item + * + * Allocates memory for entry and data + */ +int mipv6_slist_add(struct list_head *head, void *data, int datalen, + unsigned long sortkey) +{ + struct list_head *pos; + struct mipv6_sorted_list_entry *entry, *tmp, *next; + + entry = kmalloc(sizeof(struct mipv6_sorted_list_entry), GFP_ATOMIC); + + if (!entry) + return -1; + + entry->data = kmalloc(datalen, GFP_ATOMIC); + + if (!entry->data) { + kfree(entry); + return -1; + } + + memcpy(entry->data, data, datalen); + entry->datalen = datalen; + entry->sortkey = sortkey; + + if ((pos = head->next) == head) { + list_add(&entry->list, head); + return 0; + } + + tmp = list_entry(pos, struct mipv6_sorted_list_entry, list); + if (entry->sortkey < tmp->sortkey) { + list_add(&entry->list, head); + return 0; + } + + for (; pos != head; pos = pos->next) { + tmp = list_entry(pos, struct mipv6_sorted_list_entry, list); + if (pos->next == head) { + list_add(&entry->list, &tmp->list); + return 0; + } + next = list_entry(pos->next, struct mipv6_sorted_list_entry, list); + if (entry->sortkey >= tmp->sortkey && entry->sortkey < next->sortkey) { + list_add(&entry->list, &tmp->list); + return 0; + } + } + + /* never reached */ + return -1; +} + +/** + * mipv6_slist_get_first - get the first data item in the list + * @head: list_head of the sorted list + * + * Returns the actual data item, not copy, so don't kfree it + */ +void *mipv6_slist_get_first(struct list_head *head) +{ + struct mipv6_sorted_list_entry *entry; + + if (list_empty(head)) + return NULL; + + entry = list_entry(head->next, struct mipv6_sorted_list_entry, list); + return entry->data; +} + +/** + * mipv6_slist_del_first - delete (and get) the first item in list + * @head: list_head of the sorted list + * + * Remember to kfree the item + */ +void *mipv6_slist_del_first(struct list_head *head) +{ + void *tmp; + struct mipv6_sorted_list_entry *entry; + + if (list_empty(head)) + return NULL; + + entry = list_entry(head->next, struct mipv6_sorted_list_entry, list); + tmp = entry->data; + + list_del(head->next); + kfree(entry); + + return tmp; +} + +/** + * mipv6_slist_del_item - delete entry + * @head: list_head of the sorted list + * @data: item to delete + * @compare: function used for comparing the data items + * + * compare function needs to have prototype + * int (*compare)(const void *data1, const void *data2, int datalen) + */ +int mipv6_slist_del_item(struct list_head *head, void *data, + int (*compare)(const void *data1, const void *data2, + int datalen)) +{ + struct list_head *pos; + struct mipv6_sorted_list_entry *entry; + + for(pos = head->next; pos != head; pos = pos->next) { + entry = list_entry(pos, struct mipv6_sorted_list_entry, list); + if (compare(data, entry->data, entry->datalen)) { + list_del(pos); + kfree(entry->data); + kfree(entry); + return 0; + } + } + + return -1; +} + +/** + * mipv6_slist_get_first_key - get sortkey of the first item + * @head: list_head of the sorted list + */ +unsigned long mipv6_slist_get_first_key(struct list_head *head) +{ + struct mipv6_sorted_list_entry *entry; + + if (list_empty(head)) + return 0; + + entry = list_entry(head->next, struct mipv6_sorted_list_entry, list); + return entry->sortkey; +} + +/** + * mipv6_slist_get_key - get sortkey of the data item + * @head: list_head of the sorted list + * @data: the item to search for + * @compare: function used for comparing the data items + * + * compare function needs to have prototype + * int (*compare)(const void *data1, const void *data2, int datalen) + */ +unsigned long mipv6_slist_get_key(struct list_head *head, void *data, + int (*compare)(const void *data1, + const void *data2, + int datalen)) +{ + struct list_head *pos; + struct mipv6_sorted_list_entry *entry; + + for(pos = head->next; pos != head; pos = pos->next) { + entry = list_entry(pos, struct mipv6_sorted_list_entry, list); + if (compare(data, entry->data, entry->datalen)) + return entry->sortkey; + } + + return 0; +} + +/** + * mipv6_slist_get_data - get the data item identified by sortkey + * @head: list_head of the sorted list + * @key: sortkey of the item + * + * Returns the actual data item, not copy, so don't kfree it + */ +void *mipv6_slist_get_data(struct list_head *head, unsigned long sortkey) +{ + struct list_head *pos; + struct mipv6_sorted_list_entry *entry; + + list_for_each(pos, head) { + entry = list_entry(pos, struct mipv6_sorted_list_entry, list); + if (entry->sortkey == sortkey) + return entry->data; + } + + return NULL; +} + +/** + * reorder_entry - move an entry to a new position according to sortkey + * @head: list_head of the sorted list + * @entry_pos: current place of the entry + * @key: new sortkey + */ +static void reorder_entry(struct list_head *head, struct list_head *entry_pos, + unsigned long sortkey) +{ + struct list_head *pos; + struct mipv6_sorted_list_entry *entry; + + list_del(entry_pos); + + for (pos = head->next; pos != head; pos = pos->next) { + entry = list_entry(pos, struct mipv6_sorted_list_entry, list); + if (sortkey >= entry->sortkey) { + list_add(entry_pos, &entry->list); + return; + } + } + + list_add(entry_pos, head); +} + +/** + * mipv6_slist_modify - modify data item + * @head: list_head of the sorted list + * @data: item, whose sortkey is to be modified + * @datalen: datalen in bytes + * @new_key: new sortkey + * @compare: function used for comparing the data items + * + * Compies the new data on top of the old one, if compare function returns + * true. If there's no matching entry, new one will be created. + * Compare function needs to have prototype + * int (*compare)(const void *data1, const void *data2, int datalen) + */ +int mipv6_slist_modify(struct list_head *head, void *data, int datalen, + unsigned long new_key, + int (*compare)(const void *data1, const void *data2, + int datalen)) +{ + struct list_head *pos; + struct mipv6_sorted_list_entry *entry; + + for (pos = head->next; pos != head; pos = pos->next) { + entry = list_entry(pos, struct mipv6_sorted_list_entry, list); + if (compare(data, entry->data, datalen)) { + memcpy(entry->data, data, datalen); + entry->sortkey = new_key; + reorder_entry(head, &entry->list, new_key); + return 0; + } + } + + return mipv6_slist_add(head, data, datalen, new_key); +} + +/** + * mipv6_slist_push_first - move the first entry to place indicated by new_key + * @head: list_head of the sorted list + * @new_key: new sortkey + */ +int mipv6_slist_push_first(struct list_head *head, unsigned long new_key) +{ + struct mipv6_sorted_list_entry *entry; + + if (list_empty(head)) + return -1; + + entry = list_entry(head->next, struct mipv6_sorted_list_entry, list); + entry->sortkey = new_key; + + reorder_entry(head, head->next, new_key); + return 0; +} + +/** + * mipv6_slist_for_each - apply func to every item in list + * @head: list_head of the sorted list + * @args: args to pass to func + * @func: function to use + * + * function must be of type + * int (*func)(void *data, void *args, unsigned long sortkey) + * List iteration will stop once func has been applied to every item + * or when func returns true + */ +int mipv6_slist_for_each(struct list_head *head, void *args, + int (*func)(void *data, void *args, + unsigned long sortkey)) +{ + struct list_head *pos; + struct mipv6_sorted_list_entry *entry; + + list_for_each(pos, head) { + entry = list_entry(pos, struct mipv6_sorted_list_entry, list); + if (func(entry->data, args, entry->sortkey)) + break; + } + + return 0; +} diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/sortedlist.h linux-2.4.25/net/ipv6/mobile_ip6/sortedlist.h --- linux-2.4.25.old/net/ipv6/mobile_ip6/sortedlist.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.25/net/ipv6/mobile_ip6/sortedlist.h 2004-06-26 11:29:31.000000000 +0100 @@ -0,0 +1,133 @@ +/* + * Sorted list - linked list with sortkey + * + * $Id$ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +/** + * compare - compares two arbitrary data items + * @data1: first data item + * @data2: second data item + * @datalen: length of data items in bits + * + * datalen is in bits! + */ +int mipv6_bitwise_compare(const void *data1, const void *data2, int datalen); + +/** + * mipv6_slist_add - add an entry to sorted list + * @head: list_head of the sorted list + * @data: item to store + * @datalen: length of data (in bytes) + * @key: sortkey of item + * + * Allocates memory for entry and data + */ +int mipv6_slist_add(struct list_head *head, void *data, int datalen, + unsigned long sortkey); + +/** + * mipv6_slist_get_first - get the first data item in the list + * @head: list_head of the sorted list + * + * Returns the actual data item, not copy, so don't kfree it + */ +void *mipv6_slist_get_first(struct list_head *head); + +/** + * mipv6_slist_del_first - delete (and get) the first item in list + * @head: list_head of the sorted list + * + * Remember to kfree the item + */ +void *mipv6_slist_del_first(struct list_head *head); + +/** + * mipv6_slist_del_item - delete entry + * @head: list_head of the sorted list + * @data: item to delete + * @compare: function used for comparing the data items + * + * compare function needs to have prototype + * int (*compare)(const void *data1, const void *data2, int datalen) where + * datalen is in bits + */ +int mipv6_slist_del_item(struct list_head *head, void *data, + int (*compare)(const void *data1, const void *data2, + int datalen)); + +/** + * mipv6_slist_get_first_key - get sortkey of the first item + * @head: list_head of the sorted list + */ +unsigned long mipv6_slist_get_first_key(struct list_head *head); + +/** + * mipv6_slist_get_key - get sortkey of the data item + * @head: list_head of the sorted list + * @data: the item to search for + * @compare: function used for comparing the data items + * + * compare function needs to have prototype + * int (*compare)(const void *data1, const void *data2, int datalen) where + * datalen is in bits + */ +unsigned long mipv6_slist_get_key(struct list_head *head, void *data, + int (*compare)(const void *data1, + const void *data2, + int datalen)); + +/** + * mipv6_slist_get_data - get the data item identified by sortkey + * @head: list_head of the sorted list + * @key: sortkey of the item + * + * Returns the actual data item, not copy, so don't kfree it + */ +void *mipv6_slist_get_data(struct list_head *head, unsigned long sortkey); + +/** + * mipv6_slist_modify - modify data item + * @head: list_head of the sorted list + * @data: item, whose sortkey is to be modified + * @datalen: datalen in bytes + * @new_key: new sortkey + * @compare: function used for comparing the data items + * + * Compies the new data on top of the old one, if compare function returns + * non-negative. If there's no matching entry, new one will be created. + * Compare function needs to have prototype + * int (*compare)(const void *data1, const void *data2, int datalen) where + * datalen is in bits. + */ +int mipv6_slist_modify(struct list_head *head, void *data, int datalen, + unsigned long new_key, + int (*compare)(const void *data1, const void *data2, + int datalen)); + +/** + * mipv6_slist_push_first - move the first entry to place indicated by new_key + * @head: list_head of the sorted list + * @new_key: new sortkey + */ +int mipv6_slist_push_first(struct list_head *head, unsigned long new_key); + +/** + * mipv6_slist_for_each - apply func to every item in list + * @head: list_head of the sorted list + * @args: args to pass to func + * @func: function to use + * + * function must be of type + * int (*func)(void *data, void *args, unsigned long sortkey) + * List iteration will stop once func has been applied to every item + * or when func returns true + */ +int mipv6_slist_for_each(struct list_head *head, void *args, + int (*func)(void *data, void *args, + unsigned long sortkey)); diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/stats.c linux-2.4.25/net/ipv6/mobile_ip6/stats.c --- linux-2.4.25.old/net/ipv6/mobile_ip6/stats.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.25/net/ipv6/mobile_ip6/stats.c 2004-06-26 11:29:32.000000000 +0100 @@ -0,0 +1,90 @@ +/* + * Statistics module + * + * Authors: + * Sami Kivisaari + * + * $Id$ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Changes: + * Krishna Kumar, + * Venkata Jagana : SMP locking fix + */ + +#include +#include +#include "stats.h" + +struct mipv6_statistics mipv6_stats; + +static int proc_info_dump( + char *buffer, char **start, + off_t offset, int length) +{ + struct inf { + char *name; + int *value; + } int_stats[] = { + {"NEncapsulations", &mipv6_stats.n_encapsulations}, + {"NDecapsulations", &mipv6_stats.n_decapsulations}, + {"NBindRefreshRqsRcvd", &mipv6_stats.n_brr_rcvd}, + {"NHomeTestInitsRcvd", &mipv6_stats.n_hoti_rcvd}, + {"NCareofTestInitsRcvd", &mipv6_stats.n_coti_rcvd}, + {"NHomeTestRcvd", &mipv6_stats.n_hot_rcvd}, + {"NCareofTestRcvd", &mipv6_stats.n_cot_rcvd}, + {"NBindUpdatesRcvd", &mipv6_stats.n_bu_rcvd}, + {"NBindAcksRcvd", &mipv6_stats.n_ba_rcvd}, + {"NBindNAcksRcvd", &mipv6_stats.n_ban_rcvd}, + {"NBindErrorsRcvd", &mipv6_stats.n_be_rcvd}, + {"NBindRefreshRqsSent", &mipv6_stats.n_brr_sent}, + {"NHomeTestInitsSent", &mipv6_stats.n_hoti_sent}, + {"NCareofTestInitsSent", &mipv6_stats.n_coti_sent}, + {"NHomeTestSent", &mipv6_stats.n_hot_sent}, + {"NCareofTestSent", &mipv6_stats.n_cot_sent}, + {"NBindUpdatesSent", &mipv6_stats.n_bu_sent}, + {"NBindAcksSent", &mipv6_stats.n_ba_sent}, + {"NBindNAcksSent", &mipv6_stats.n_ban_sent}, + {"NBindErrorsSent", &mipv6_stats.n_be_sent}, + {"NBindUpdatesDropAuth", &mipv6_stats.n_bu_drop.auth}, + {"NBindUpdatesDropInvalid", &mipv6_stats.n_bu_drop.invalid}, + {"NBindUpdatesDropMisc", &mipv6_stats.n_bu_drop.misc}, + {"NBindAcksDropAuth", &mipv6_stats.n_bu_drop.auth}, + {"NBindAcksDropInvalid", &mipv6_stats.n_bu_drop.invalid}, + {"NBindAcksDropMisc", &mipv6_stats.n_bu_drop.misc}, + {"NBindRqsDropAuth", &mipv6_stats.n_bu_drop.auth}, + {"NBindRqsDropInvalid", &mipv6_stats.n_bu_drop.invalid}, + {"NBindRqsDropMisc", &mipv6_stats.n_bu_drop.misc} + }; + + int i; + int len = 0; + for(i=0; i length) len = length; + + return len; +} + +int mipv6_stats_init(void) +{ + memset(&mipv6_stats, 0, sizeof(struct mipv6_statistics)); + proc_net_create("mip6_stat", 0, proc_info_dump); + return 0; +} + +void mipv6_stats_exit(void) +{ + proc_net_remove("mip6_stat"); +} diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/stats.h linux-2.4.25/net/ipv6/mobile_ip6/stats.h --- linux-2.4.25.old/net/ipv6/mobile_ip6/stats.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.25/net/ipv6/mobile_ip6/stats.h 2004-06-26 11:29:32.000000000 +0100 @@ -0,0 +1,71 @@ +/* + * MIPL Mobile IPv6 Statistics header file + * + * $Id$ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _STATS_H +#define _STATS_H + +struct mipv6_drop { + __u32 auth; + __u32 invalid; + __u32 misc; +}; + +struct mipv6_statistics { + int n_encapsulations; + int n_decapsulations; + int n_mh_in_msg; + int n_mh_in_error; + int n_mh_out_msg; + int n_mh_out_error; + + int n_brr_rcvd; + int n_hoti_rcvd; + int n_coti_rcvd; + int n_hot_rcvd; + int n_cot_rcvd; + int n_bu_rcvd; + int n_ba_rcvd; + int n_ban_rcvd; + int n_be_rcvd; + + int n_brr_sent; + int n_hoti_sent; + int n_coti_sent; + int n_hot_sent; + int n_cot_sent; + int n_bu_sent; + int n_ba_sent; + int n_ban_sent; + int n_be_sent; + + int n_ha_rcvd; + int n_ha_sent; + + struct mipv6_drop n_bu_drop; + struct mipv6_drop n_ba_drop; + struct mipv6_drop n_brr_drop; + struct mipv6_drop n_be_drop; + struct mipv6_drop n_ha_drop; +}; + +extern struct mipv6_statistics mipv6_stats; + +#ifdef CONFIG_SMP +/* atomic_t is max 24 bits long */ +#define MIPV6_INC_STATS(X) atomic_inc((atomic_t *)&mipv6_stats.X); +#else +#define MIPV6_INC_STATS(X) mipv6_stats.X++; +#endif + +int mipv6_stats_init(void); +void mipv6_stats_exit(void); + +#endif diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/tunnel.h linux-2.4.25/net/ipv6/mobile_ip6/tunnel.h --- linux-2.4.25.old/net/ipv6/mobile_ip6/tunnel.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.25/net/ipv6/mobile_ip6/tunnel.h 2004-06-26 11:29:32.000000000 +0100 @@ -0,0 +1,35 @@ +/* + * MIPL Mobile IPv6 IP6-IP6 tunneling header file + * + * $Id$ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _TUNNEL_H +#define _TUNNEL_H + +#include +#include +#include + +static __inline__ int is_mip6_tnl(struct ip6_tnl *t) +{ + return (t != NULL && + t->parms.flags & IP6_TNL_F_KERNEL_DEV && + t->parms.flags & IP6_TNL_F_MIP6_DEV); + +} + +static __inline__ int dev_is_mip6_tnl(struct net_device *dev) +{ + struct ip6_tnl *t = (struct ip6_tnl *)dev->priv; + return (dev->type == ARPHRD_TUNNEL6 && is_mip6_tnl(t)); +} + + +#endif + diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/tunnel_ha.c linux-2.4.25/net/ipv6/mobile_ip6/tunnel_ha.c --- linux-2.4.25.old/net/ipv6/mobile_ip6/tunnel_ha.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.25/net/ipv6/mobile_ip6/tunnel_ha.c 2004-06-26 11:29:32.000000000 +0100 @@ -0,0 +1,264 @@ +/* + * IPv6-IPv6 tunneling module + * + * Authors: + * Sami Kivisaari + * Ville Nuorvala + * + * $Id$ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_SYSCTL +#include +#endif /* CONFIG_SYSCTL */ + +#include +#include +#include +#include +#include + +#include "tunnel.h" +#include "debug.h" +#include "stats.h" +#include "config.h" + +#define MIPV6_TNL_MAX IP6_TNL_MAX +#define MIPV6_TNL_MIN 1 + +int mipv6_max_tnls = 3; +int mipv6_min_tnls = 1; + +DECLARE_MUTEX(tnl_sem); + +int mipv6_max_tnls_sysctl(ctl_table *ctl, int write, struct file *filp, + void *buffer, size_t *lenp) +{ + int err; + + DEBUG_FUNC(); + + down(&tnl_sem); + if (write) { + int diff; + int old_max_tnls = mipv6_max_tnls; + err = proc_dointvec(ctl, write, filp, buffer, lenp); + if (err < 0) + goto out; + if (mipv6_max_tnls < mipv6_min_tnls || + mipv6_max_tnls > MIPV6_TNL_MAX) { + mipv6_max_tnls = old_max_tnls; + goto out; + } + if (mipv6_max_tnls < old_max_tnls) { + diff = old_max_tnls - mipv6_max_tnls; + ip6ip6_tnl_dec_max_kdev_count(diff); + } else if (mipv6_max_tnls > old_max_tnls) { + diff = mipv6_max_tnls - old_max_tnls; + ip6ip6_tnl_inc_max_kdev_count(diff); + } + } else { + err = proc_dointvec(ctl, write, filp, buffer, lenp); + } +out: + up(&tnl_sem); + return err; +} + +int mipv6_min_tnls_sysctl(ctl_table *ctl, int write, struct file *filp, + void *buffer, size_t *lenp) +{ + int err; + + DEBUG_FUNC(); + + down(&tnl_sem); + if (write) { + int diff; + int old_min_tnls = mipv6_min_tnls; + err = proc_dointvec(ctl, write, filp, buffer, lenp); + if (err < 0) + goto out; + if (mipv6_min_tnls > mipv6_max_tnls || + mipv6_min_tnls < MIPV6_TNL_MIN) { + mipv6_min_tnls = old_min_tnls; + goto out; + } + if (mipv6_min_tnls < old_min_tnls) { + diff = old_min_tnls - mipv6_min_tnls; + ip6ip6_tnl_dec_min_kdev_count(diff); + } else if (mipv6_min_tnls > old_min_tnls) { + diff = mipv6_min_tnls - old_min_tnls; + ip6ip6_tnl_inc_min_kdev_count(diff); + } + } else { + err = proc_dointvec(ctl, write, filp, buffer, lenp); + } +out: + up(&tnl_sem); + return err; +} + +static __inline__ int mipv6_tnl_add(struct in6_addr *remote, + struct in6_addr *local) +{ + struct ip6_tnl_parm p; + int ret; + + DEBUG_FUNC(); + + memset(&p, 0, sizeof(p)); + p.proto = IPPROTO_IPV6; + ipv6_addr_copy(&p.laddr, local); + ipv6_addr_copy(&p.raddr, remote); + p.hop_limit = 255; + p.flags = (IP6_TNL_F_KERNEL_DEV | IP6_TNL_F_MIP6_DEV | + IP6_TNL_F_IGN_ENCAP_LIMIT); + + ret = ip6ip6_kernel_tnl_add(&p); + if (ret > 0) { + DEBUG(DBG_INFO, "added tunnel from: " + "%x:%x:%x:%x:%x:%x:%x:%x to: %x:%x:%x:%x:%x:%x:%x:%x", + NIPV6ADDR(local), NIPV6ADDR(remote)); + } else { + DEBUG(DBG_WARNING, "unable to add tunnel from: " + "%x:%x:%x:%x:%x:%x:%x:%x to: %x:%x:%x:%x:%x:%x:%x:%x", + NIPV6ADDR(local), NIPV6ADDR(remote)); + } + return ret; +} + +static __inline__ int mipv6_tnl_del(struct in6_addr *remote, + struct in6_addr *local) +{ + struct ip6_tnl *t = ip6ip6_tnl_lookup(remote, local); + + DEBUG_FUNC(); + + if (t != NULL && (t->parms.flags & IP6_TNL_F_MIP6_DEV)) { + DEBUG(DBG_INFO, "deleting tunnel from: " + "%x:%x:%x:%x:%x:%x:%x:%x to: %x:%x:%x:%x:%x:%x:%x:%x", + NIPV6ADDR(local), NIPV6ADDR(remote)); + + return ip6ip6_kernel_tnl_del(t); + } + return 0; +} + +static int add_route_to_mn(struct in6_addr *coa, struct in6_addr *ha_addr, + struct in6_addr *home_addr) +{ + struct in6_rtmsg rtmsg; + int err; + struct ip6_tnl *t = ip6ip6_tnl_lookup(coa, ha_addr); + + if (!is_mip6_tnl(t)) { + DEBUG(DBG_CRITICAL,"Tunnel missing"); + return -ENODEV; + } + + DEBUG(DBG_INFO, "adding route to: %x:%x:%x:%x:%x:%x:%x:%x via " + "tunnel device", NIPV6ADDR(home_addr)); + + memset(&rtmsg, 0, sizeof(rtmsg)); + ipv6_addr_copy(&rtmsg.rtmsg_dst, home_addr); + rtmsg.rtmsg_dst_len = 128; + rtmsg.rtmsg_type = RTMSG_NEWROUTE; + rtmsg.rtmsg_flags = RTF_UP | RTF_NONEXTHOP | RTF_HOST | RTF_MOBILENODE; + rtmsg.rtmsg_ifindex = t->dev->ifindex; + rtmsg.rtmsg_metric = IP6_RT_PRIO_MIPV6; + if ((err = ip6_route_add(&rtmsg, NULL)) == -EEXIST) { + err = 0; + } + return err; +} + +static void del_route_to_mn(struct in6_addr *coa, struct in6_addr *ha_addr, + struct in6_addr *home_addr) +{ + struct ip6_tnl *t = ip6ip6_tnl_lookup(coa, ha_addr); + + DEBUG_FUNC(); + + if (is_mip6_tnl(t)) { + struct in6_rtmsg rtmsg; + + DEBUG(DBG_INFO, "deleting route to: %x:%x:%x:%x:%x:%x:%x:%x " + " via tunnel device", NIPV6ADDR(home_addr)); + + memset(&rtmsg, 0, sizeof(rtmsg)); + ipv6_addr_copy(&rtmsg.rtmsg_dst, home_addr); + rtmsg.rtmsg_dst_len = 128; + rtmsg.rtmsg_ifindex = t->dev->ifindex; + rtmsg.rtmsg_metric = IP6_RT_PRIO_MIPV6; + ip6_route_del(&rtmsg, NULL); + } +} + + +int mipv6_add_tnl_to_mn(struct in6_addr *coa, + struct in6_addr *ha_addr, + struct in6_addr *home_addr) +{ + int ret; + + DEBUG_FUNC(); + + ret = mipv6_tnl_add(coa, ha_addr); + + if (ret > 0) { + int err = add_route_to_mn(coa, ha_addr, home_addr); + if (err) { + if (err != -ENODEV) { + mipv6_tnl_del(coa, ha_addr); + } + return err; + } + } + return ret; +} + +int mipv6_del_tnl_to_mn(struct in6_addr *coa, + struct in6_addr *ha_addr, + struct in6_addr *home_addr) +{ + DEBUG_FUNC(); + del_route_to_mn(coa, ha_addr, home_addr); + return mipv6_tnl_del(coa, ha_addr); +} + +__init void mipv6_initialize_tunnel(void) +{ + down(&tnl_sem); + ip6ip6_tnl_inc_max_kdev_count(mipv6_max_tnls); + ip6ip6_tnl_inc_min_kdev_count(mipv6_min_tnls); + up(&tnl_sem); + mip6_fn.bce_tnl_rt_add = add_route_to_mn; + mip6_fn.bce_tnl_rt_del = del_route_to_mn; +} + +__exit void mipv6_shutdown_tunnel(void) +{ + mip6_fn.bce_tnl_rt_del = NULL; + mip6_fn.bce_tnl_rt_add = NULL; + down(&tnl_sem); + ip6ip6_tnl_dec_min_kdev_count(mipv6_min_tnls); + ip6ip6_tnl_dec_max_kdev_count(mipv6_max_tnls); + up(&tnl_sem); +} + diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/tunnel_ha.h linux-2.4.25/net/ipv6/mobile_ip6/tunnel_ha.h --- linux-2.4.25.old/net/ipv6/mobile_ip6/tunnel_ha.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.25/net/ipv6/mobile_ip6/tunnel_ha.h 2004-06-26 11:29:32.000000000 +0100 @@ -0,0 +1,20 @@ +#ifndef _TUNNEL_HA_H +#define _TUNNEL_HA_H + +#include "tunnel.h" + +extern int mipv6_max_tnls; +extern int mipv6_min_tnls; + +extern void mipv6_initialize_tunnel(void); +extern void mipv6_shutdown_tunnel(void); + +extern int mipv6_add_tnl_to_mn(struct in6_addr *coa, + struct in6_addr *ha_addr, + struct in6_addr *home_addr); + +extern int mipv6_del_tnl_to_mn(struct in6_addr *coa, + struct in6_addr *ha_addr, + struct in6_addr *home_addr); + +#endif diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/tunnel_mn.c linux-2.4.25/net/ipv6/mobile_ip6/tunnel_mn.c --- linux-2.4.25.old/net/ipv6/mobile_ip6/tunnel_mn.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.25/net/ipv6/mobile_ip6/tunnel_mn.c 2004-06-26 11:29:32.000000000 +0100 @@ -0,0 +1,160 @@ +/* + * IPv6-IPv6 tunneling module + * + * Authors: + * Sami Kivisaari + * Ville Nuorvala + * + * $Id$ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_SYSCTL +#include +#endif /* CONFIG_SYSCTL */ + +#include +#include +#include +#include +#include + +#include "tunnel.h" +#include "debug.h" +#include "stats.h" + +static struct net_device *mn_ha_tdev; + +static spinlock_t mn_ha_lock = SPIN_LOCK_UNLOCKED; + +static __inline__ int add_reverse_route(struct in6_addr *ha_addr, + struct in6_addr *home_addr, + struct net_device *tdev) +{ + struct in6_rtmsg rtmsg; + int err; + + DEBUG_FUNC(); + + memset(&rtmsg, 0, sizeof(rtmsg)); + rtmsg.rtmsg_type = RTMSG_NEWROUTE; + ipv6_addr_copy(&rtmsg.rtmsg_src, home_addr); + rtmsg.rtmsg_src_len = 128; + rtmsg.rtmsg_flags = RTF_UP | RTF_DEFAULT; + rtmsg.rtmsg_ifindex = tdev->ifindex; + rtmsg.rtmsg_metric = IP6_RT_PRIO_MIPV6; + if ((err = ip6_route_add(&rtmsg, NULL)) == -EEXIST) { + return 0; + } + return err; +} + +static __inline__ void del_reverse_route(struct in6_addr *ha_addr, + struct in6_addr *home_addr, + struct net_device *tdev) +{ + struct in6_rtmsg rtmsg; + + DEBUG(DBG_INFO, "removing reverse route via tunnel device"); + + memset(&rtmsg, 0, sizeof(rtmsg)); + ipv6_addr_copy(&rtmsg.rtmsg_src, home_addr); + rtmsg.rtmsg_src_len = 128; + rtmsg.rtmsg_ifindex = tdev->ifindex; + rtmsg.rtmsg_metric = IP6_RT_PRIO_MIPV6; + ip6_route_del(&rtmsg, NULL); +} + +int mipv6_add_tnl_to_ha(void) +{ + struct ip6_tnl_parm p; + struct ip6_tnl *t; + int err; + + DEBUG_FUNC(); + + memset(&p, 0, sizeof(p)); + p.proto = IPPROTO_IPV6; + p.hop_limit = 255; + p.flags = (IP6_TNL_F_KERNEL_DEV | IP6_TNL_F_MIP6_DEV | + IP6_TNL_F_IGN_ENCAP_LIMIT); + strcpy(p.name, "mip6mnha1"); + + rtnl_lock(); + if ((err = ip6ip6_tnl_create(&p, &t))) { + rtnl_unlock(); + return err; + } + spin_lock_bh(&mn_ha_lock); + + if (!mn_ha_tdev) { + mn_ha_tdev = t->dev; + dev_hold(mn_ha_tdev); + } + spin_unlock_bh(&mn_ha_lock); + dev_open(t->dev); + rtnl_unlock(); + return 0; +} + +int mipv6_mv_tnl_to_ha(struct in6_addr *ha_addr, + struct in6_addr *coa, + struct in6_addr *home_addr) +{ + int err = -ENODEV; + + DEBUG_FUNC(); + + spin_lock_bh(&mn_ha_lock); + if (mn_ha_tdev) { + struct ip6_tnl_parm p; + memset(&p, 0, sizeof(p)); + p.proto = IPPROTO_IPV6; + ipv6_addr_copy(&p.laddr, coa); + ipv6_addr_copy(&p.raddr, ha_addr); + p.hop_limit = 255; + p.flags = (IP6_TNL_F_KERNEL_DEV | IP6_TNL_F_MIP6_DEV | + IP6_TNL_F_IGN_ENCAP_LIMIT); + + ip6ip6_tnl_change((struct ip6_tnl *) mn_ha_tdev->priv, &p); + if (ipv6_addr_cmp(coa, home_addr)) { + err = add_reverse_route(ha_addr, home_addr, + mn_ha_tdev); + } else { + del_reverse_route(ha_addr, home_addr, mn_ha_tdev); + err = 0; + } + } + spin_unlock_bh(&mn_ha_lock); + return err; +} + +void mipv6_del_tnl_to_ha(void) +{ + struct net_device *dev; + + DEBUG_FUNC(); + + rtnl_lock(); + spin_lock_bh(&mn_ha_lock); + dev = mn_ha_tdev; + mn_ha_tdev = NULL; + spin_unlock_bh(&mn_ha_lock); + dev_put(dev); + unregister_netdevice(dev); + rtnl_unlock(); +} diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/tunnel_mn.h linux-2.4.25/net/ipv6/mobile_ip6/tunnel_mn.h --- linux-2.4.25.old/net/ipv6/mobile_ip6/tunnel_mn.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.25/net/ipv6/mobile_ip6/tunnel_mn.h 2004-06-26 11:29:32.000000000 +0100 @@ -0,0 +1,14 @@ +#ifndef _TUNNEL_MN_H +#define _TUNNEL_MN_H + +#include "tunnel.h" + +extern int mipv6_add_tnl_to_ha(void); + +extern int mipv6_mv_tnl_to_ha(struct in6_addr *ha_addr, + struct in6_addr *coa, + struct in6_addr *home_addr); + +extern int mipv6_del_tnl_to_ha(void); + +#endif diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/util.h linux-2.4.25/net/ipv6/mobile_ip6/util.h --- linux-2.4.25.old/net/ipv6/mobile_ip6/util.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.4.25/net/ipv6/mobile_ip6/util.h 2004-06-26 11:29:32.000000000 +0100 @@ -0,0 +1,91 @@ +/* + * MIPL Mobile IPv6 Utility functions + * + * $Id$ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _UTIL_H +#define _UTIL_H + +#include +#include + +/** + * mipv6_prefix_compare - Compare two IPv6 prefixes + * @addr: IPv6 address + * @prefix: IPv6 address + * @nprefix: number of bits to compare + * + * Perform prefix comparison bitwise for the @nprefix first bits + * Returns 1, if the prefixes are the same, 0 otherwise + **/ +static inline int mipv6_prefix_compare(const struct in6_addr *addr, + const struct in6_addr *prefix, + const unsigned int pfix_len) +{ + int i; + unsigned int nprefix = pfix_len; + + if (nprefix > 128) + return 0; + + for (i = 0; nprefix > 0; nprefix -= 32, i++) { + if (nprefix >= 32) { + if (addr->s6_addr32[i] != prefix->s6_addr32[i]) + return 0; + } else { + if (((addr->s6_addr32[i] ^ prefix->s6_addr32[i]) & + ((~0) << (32 - nprefix))) != 0) + return 0; + return 1; + } + } + + return 1; +} + +/** + * homeagent_anycast - Compute Home Agent anycast address + * @ac_addr: append home agent anycast suffix to passed prefix + * @prefix: prefix ha anycast address is generated from + * @plen: length of prefix in bits + * + * Calculate corresponding Home Agent Anycast Address (RFC2526) in a + * given subnet. + */ +static inline int +mipv6_ha_anycast(struct in6_addr *ac_addr, struct in6_addr *prefix, int plen) +{ + if (plen <= 0 || plen > 120) { + /* error, interface id should be minimum 8 bits */ + return -1; + } + ipv6_addr_copy(ac_addr, prefix); + + if (plen < 32) + ac_addr->s6_addr32[0] |= htonl((u32)(~0) >> plen); + if (plen < 64) + ac_addr->s6_addr32[1] |= htonl((u32)(~0) >> (plen > 32 ? plen % 32 : 0)); + if (plen < 92) + ac_addr->s6_addr32[2] |= htonl((u32)(~0) >> (plen > 64 ? plen % 32 : 0)); + if (plen <= 120) + ac_addr->s6_addr32[3] |= htonl((u32)(~0) >> (plen > 92 ? plen % 32 : 0)); + + /* RFC2526: for interface identifiers in EUI-64 + * format, the universal/local bit in the interface + * identifier MUST be set to 0. */ + if (plen == 64) { + ac_addr->s6_addr32[2] &= (int)htonl(0xfdffffff); + } + /* Mobile IPv6 Home-Agents anycast id (0x7e) */ + ac_addr->s6_addr32[3] &= (int)htonl(0xfffffffe); + + return 0; +} + +#endif /* _UTIL_H */ diff -uprN linux-2.4.25.old/net/ipv6/ndisc.c linux-2.4.25/net/ipv6/ndisc.c --- linux-2.4.25.old/net/ipv6/ndisc.c 2003-11-28 18:26:21.000000000 +0000 +++ linux-2.4.25/net/ipv6/ndisc.c 2004-06-26 11:29:32.000000000 +0100 @@ -23,6 +23,7 @@ * and moved to net/core. * Pekka Savola : RFC2461 validation * YOSHIFUJI Hideaki @USAGI : Verify ND options properly + * Ville Nuorvala : RFC2461 fixes to proxy ND */ /* Set to 3 to get tracing... */ @@ -70,6 +71,7 @@ #include #include #include +#include #include #include @@ -187,6 +189,8 @@ struct ndisc_options *ndisc_parse_option case ND_OPT_TARGET_LL_ADDR: case ND_OPT_MTU: case ND_OPT_REDIRECT_HDR: + case ND_OPT_RTR_ADV_INTERVAL: + case ND_OPT_HOME_AGENT_INFO: if (ndopts->nd_opt_array[nd_opt->nd_opt_type]) { ND_PRINTK2((KERN_WARNING "ndisc_parse_options(): duplicated ND6 option found: type=%d\n", @@ -372,8 +376,8 @@ ndisc_build_ll_hdr(struct sk_buff *skb, */ void ndisc_send_na(struct net_device *dev, struct neighbour *neigh, - struct in6_addr *daddr, struct in6_addr *solicited_addr, - int router, int solicited, int override, int inc_opt) + struct in6_addr *daddr, struct in6_addr *solicited_addr, + int router, int solicited, int override, int inc_opt) { static struct in6_addr tmpaddr; struct inet6_ifaddr *ifp; @@ -766,7 +770,8 @@ void ndisc_recv_ns(struct sk_buff *skb) int addr_type = ipv6_addr_type(saddr); if (in6_dev && in6_dev->cnf.forwarding && - (addr_type & IPV6_ADDR_UNICAST) && + (addr_type & IPV6_ADDR_UNICAST || + addr_type == IPV6_ADDR_ANY) && pneigh_lookup(&nd_tbl, &msg->target, dev, 0)) { int inc = ipv6_addr_type(daddr)&IPV6_ADDR_MULTICAST; @@ -778,13 +783,21 @@ void ndisc_recv_ns(struct sk_buff *skb) nd_tbl.stats.rcv_probes_mcast++; else nd_tbl.stats.rcv_probes_ucast++; - - neigh = neigh_event_ns(&nd_tbl, lladdr, saddr, dev); - if (neigh) { - ndisc_send_na(dev, neigh, saddr, &msg->target, - 0, 1, 0, 1); - neigh_release(neigh); + if (addr_type & IPV6_ADDR_UNICAST) { + neigh = neigh_event_ns(&nd_tbl, lladdr, saddr, dev); + + if (neigh) { + ndisc_send_na(dev, neigh, saddr, &msg->target, + 0, 1, 0, 1); + neigh_release(neigh); + } + } else { + /* the proxy should also protect against DAD */ + struct in6_addr maddr; + ipv6_addr_all_nodes(&maddr); + ndisc_send_na(dev, NULL, &maddr, &msg->target, + 0, 0, 0, 1); } } else { struct sk_buff *n = skb_clone(skb, GFP_ATOMIC); @@ -849,6 +862,9 @@ void ndisc_recv_na(struct sk_buff *skb) if (ifp->flags & IFA_F_TENTATIVE) { addrconf_dad_failure(ifp); return; + } else if (ndisc_mip_mn_ha_probe(ifp, lladdr)) { + in6_ifa_put(ifp); + return; } /* What should we make now? The advertisement is invalid, but ndisc specs say nothing @@ -887,6 +903,7 @@ void ndisc_recv_na(struct sk_buff *skb) msg->icmph.icmp6_override, 1); neigh_release(neigh); } + ndisc_check_mipv6_dad(&msg->target); } static void ndisc_router_discovery(struct sk_buff *skb) @@ -894,6 +911,7 @@ static void ndisc_router_discovery(struc struct ra_msg *ra_msg = (struct ra_msg *) skb->h.raw; struct neighbour *neigh; struct inet6_dev *in6_dev; + int change_rtr; struct rt6_info *rt; int lifetime; struct ndisc_options ndopts; @@ -923,10 +941,6 @@ static void ndisc_router_discovery(struc ND_PRINTK1("RA: can't find in6 device\n"); return; } - if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_ra) { - in6_dev_put(in6_dev); - return; - } if (!ndisc_parse_options(opt, optlen, &ndopts)) { in6_dev_put(in6_dev); @@ -935,7 +949,12 @@ static void ndisc_router_discovery(struc "ICMP6 RA: invalid ND option, ignored.\n"); return; } + change_rtr = ndisc_mipv6_ra_rcv(skb, &ndopts); + if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_ra) { + in6_dev_put(in6_dev); + return; + } if (in6_dev->if_flags & IF_RS_SENT) { /* * flag that an RA was received after an RS was sent @@ -963,8 +982,7 @@ static void ndisc_router_discovery(struc ip6_del_rt(rt, NULL); rt = NULL; } - - if (rt == NULL && lifetime) { + if (rt == NULL && lifetime && change_rtr) { ND_PRINTK2("ndisc_rdisc: adding default router\n"); rt = rt6_add_dflt_router(&skb->nh.ipv6h->saddr, skb->dev); @@ -1087,6 +1105,8 @@ out: if (rt) dst_release(&rt->u.dst); in6_dev_put(in6_dev); + + ndisc_mipv6_change_router(change_rtr); } static void ndisc_redirect_rcv(struct sk_buff *skb) diff -uprN linux-2.4.25.old/net/ipv6/raw.c linux-2.4.25/net/ipv6/raw.c --- linux-2.4.25.old/net/ipv6/raw.c 2003-11-28 18:26:21.000000000 +0000 +++ linux-2.4.25/net/ipv6/raw.c 2004-06-26 11:29:32.000000000 +0100 @@ -43,6 +43,7 @@ #include #include #include +#include #include @@ -636,6 +637,7 @@ static int rawv6_sendmsg(struct sock *sk hdr.daddr = daddr; else hdr.daddr = NULL; + hdr.daddr = mipv6_get_fake_hdr_daddr(hdr.daddr, daddr); err = ip6_build_xmit(sk, rawv6_frag_cksum, &hdr, &fl, len, opt, hlimit, msg->msg_flags); diff -uprN linux-2.4.25.old/net/ipv6/route.c linux-2.4.25/net/ipv6/route.c --- linux-2.4.25.old/net/ipv6/route.c 2004-02-18 13:36:32.000000000 +0000 +++ linux-2.4.25/net/ipv6/route.c 2004-06-26 11:29:32.000000000 +0100 @@ -49,6 +49,7 @@ #include #include #include +#include #include @@ -363,12 +364,8 @@ static struct rt6_info *rt6_cow(struct r rt->u.dst.flags |= DST_HOST; #ifdef CONFIG_IPV6_SUBTREES - if (rt->rt6i_src.plen && saddr) { - ipv6_addr_copy(&rt->rt6i_src.addr, saddr); - rt->rt6i_src.plen = 128; - } + rt->rt6i_src.plen = ort->rt6i_src.plen; #endif - rt->rt6i_nexthop = ndisc_get_neigh(rt->rt6i_dev, &rt->rt6i_gateway); dst_hold(&rt->u.dst); @@ -511,14 +508,19 @@ struct dst_entry * ip6_route_output(stru struct rt6_info *rt; int strict; int attempts = 3; + struct in6_addr *saddr; + if (ipv6_chk_addr(fl->nl_u.ip6_u.daddr, NULL)) + saddr = NULL; + else + saddr = fl->nl_u.ip6_u.saddr; + strict = ipv6_addr_type(fl->nl_u.ip6_u.daddr) & (IPV6_ADDR_MULTICAST|IPV6_ADDR_LINKLOCAL); relookup: read_lock_bh(&rt6_lock); - fn = fib6_lookup(&ip6_routing_table, fl->nl_u.ip6_u.daddr, - fl->nl_u.ip6_u.saddr); + fn = fib6_lookup(&ip6_routing_table, fl->nl_u.ip6_u.daddr, saddr); restart: rt = fn->leaf; @@ -663,25 +665,6 @@ out: return (atomic_read(&ip6_dst_ops.entries) > ip6_rt_max_size); } -/* Clean host part of a prefix. Not necessary in radix tree, - but results in cleaner routing tables. - - Remove it only when all the things will work! - */ - -static void ipv6_addr_prefix(struct in6_addr *pfx, - const struct in6_addr *addr, int plen) -{ - int b = plen&0x7; - int o = plen>>3; - - memcpy(pfx->s6_addr, addr, o); - if (o < 16) - memset(pfx->s6_addr + o, 0, 16 - o); - if (b != 0) - pfx->s6_addr[o] = addr->s6_addr[o]&(0xff00 >> b); -} - static int ipv6_get_mtu(struct net_device *dev) { int mtu = IPV6_MIN_MTU; @@ -810,7 +793,7 @@ int ip6_route_add(struct in6_rtmsg *rtms if (!(gwa_type&IPV6_ADDR_UNICAST)) goto out; - grt = rt6_lookup(gw_addr, NULL, rtmsg->rtmsg_ifindex, 1); + grt = rt6_lookup(gw_addr, &rtmsg->rtmsg_src, rtmsg->rtmsg_ifindex, 1); err = -EHOSTUNREACH; if (grt == NULL) @@ -848,7 +831,15 @@ int ip6_route_add(struct in6_rtmsg *rtms goto out; } } - +#ifdef USE_IPV6_MOBILITY + /* If destination is mobile node, add special skb->dst->input + * function for proxy ND. + */ + if (rtmsg->rtmsg_flags & RTF_MOBILENODE) { + rt->u.dst.input = ip6_mipv6_forward; + } +#endif /* CONFIG_IPV6_MOBILITY */ + if (ipv6_addr_is_multicast(&rt->rt6i_dst.addr)) rt->rt6i_hoplimit = IPV6_DEFAULT_MCASTHOPS; else @@ -936,7 +927,7 @@ void rt6_redirect(struct in6_addr *dest, struct rt6_info *rt, *nrt; /* Locate old route to this destination. */ - rt = rt6_lookup(dest, NULL, neigh->dev->ifindex, 1); + rt = rt6_lookup(dest, saddr, neigh->dev->ifindex, 1); if (rt == NULL) return; @@ -1003,6 +994,9 @@ source_ok: nrt = ip6_rt_copy(rt); if (nrt == NULL) goto out; +#ifdef CONFIG_IPV6_SUBTREES + nrt->rt6i_src.plen = rt->rt6i_src.plen; +#endif nrt->rt6i_flags = RTF_GATEWAY|RTF_UP|RTF_DYNAMIC|RTF_CACHE; if (on_link) @@ -1104,6 +1098,9 @@ void rt6_pmtu_discovery(struct in6_addr nrt = ip6_rt_copy(rt); if (nrt == NULL) goto out; +#ifdef CONFIG_IPV6_SUBTREES + nrt->rt6i_src.plen = rt->rt6i_src.plen; +#endif ipv6_addr_copy(&nrt->rt6i_dst.addr, daddr); nrt->rt6i_dst.plen = 128; nrt->u.dst.flags |= DST_HOST; diff -uprN linux-2.4.25.old/net/ipv6/tcp_ipv6.c linux-2.4.25/net/ipv6/tcp_ipv6.c --- linux-2.4.25.old/net/ipv6/tcp_ipv6.c 2003-11-28 18:26:21.000000000 +0000 +++ linux-2.4.25/net/ipv6/tcp_ipv6.c 2004-06-26 11:29:32.000000000 +0100 @@ -50,6 +50,7 @@ #include #include #include +#include #include @@ -557,6 +558,7 @@ static int tcp_v6_connect(struct sock *s struct flowi fl; struct dst_entry *dst; int addr_type; + int reroute = 0; int err; if (addr_len < SIN6_LEN_RFC2133) @@ -660,7 +662,7 @@ static int tcp_v6_connect(struct sock *s fl.proto = IPPROTO_TCP; fl.fl6_dst = &np->daddr; - fl.fl6_src = saddr; + fl.fl6_src = saddr; fl.oif = sk->bound_dev_if; fl.uli_u.ports.dport = usin->sin6_port; fl.uli_u.ports.sport = sk->sport; @@ -669,31 +671,46 @@ static int tcp_v6_connect(struct sock *s struct rt0_hdr *rt0 = (struct rt0_hdr *) np->opt->srcrt; fl.nl_u.ip6_u.daddr = rt0->addr; } - dst = ip6_route_output(sk, &fl); - +#ifdef CONFIG_IPV6_SUBTREES + reroute = (saddr == NULL); +#endif if ((err = dst->error) != 0) { dst_release(dst); goto failure; } - - ip6_dst_store(sk, dst, NULL); - sk->route_caps = dst->dev->features&~NETIF_F_IP_CSUM; - + if (!reroute) { + ip6_dst_store(sk, dst, NULL, NULL); + sk->route_caps = dst->dev->features&~NETIF_F_IP_CSUM; + } if (saddr == NULL) { err = ipv6_get_saddr(dst, &np->daddr, &saddr_buf); + + if (reroute) + dst_release(dst); if (err) goto failure; saddr = &saddr_buf; + ipv6_addr_copy(&np->rcv_saddr, saddr); +#ifdef CONFIG_IPV6_SUBTREES + fl.fl6_src = saddr; + dst = ip6_route_output(sk, &fl); + + if ((err = dst->error) != 0) { + dst_release(dst); + goto failure; + } + ip6_dst_store(sk, dst, NULL, NULL); + sk->route_caps = dst->dev->features&~NETIF_F_IP_CSUM; +#endif } /* set the source address */ - ipv6_addr_copy(&np->rcv_saddr, saddr); ipv6_addr_copy(&np->saddr, saddr); sk->rcv_saddr= LOOPBACK4_IPV6; - tp->ext_header_len = 0; + tp->ext_header_len = tcp_v6_get_mipv6_header_len(); if (np->opt) tp->ext_header_len = np->opt->opt_flen+np->opt->opt_nflen; tp->mss_clamp = IPV6_MIN_MTU - sizeof(struct tcphdr) - sizeof(struct ipv6hdr); @@ -1338,7 +1355,7 @@ static struct sock * tcp_v6_syn_recv_soc #endif MOD_INC_USE_COUNT; - ip6_dst_store(newsk, dst, NULL); + ip6_dst_store(newsk, dst, NULL, NULL); sk->route_caps = dst->dev->features&~NETIF_F_IP_CSUM; newtp = &(newsk->tp_pinfo.af_tcp); @@ -1383,7 +1400,7 @@ static struct sock * tcp_v6_syn_recv_soc sock_kfree_s(sk, opt, opt->tot_len); } - newtp->ext_header_len = 0; + newtp->ext_header_len = tcp_v6_get_mipv6_header_len(); if (np->opt) newtp->ext_header_len = np->opt->opt_nflen + np->opt->opt_flen; @@ -1710,7 +1727,7 @@ static int tcp_v6_rebuild_header(struct return err; } - ip6_dst_store(sk, dst, NULL); + ip6_dst_store(sk, dst, NULL, NULL); sk->route_caps = dst->dev->features&~NETIF_F_IP_CSUM; } @@ -1749,7 +1766,7 @@ static int tcp_v6_xmit(struct sk_buff *s return -sk->err_soft; } - ip6_dst_store(sk, dst, NULL); + ip6_dst_store(sk, dst, NULL, NULL); } skb->dst = dst_clone(dst); diff -uprN linux-2.4.25.old/net/ipv6/udp.c linux-2.4.25/net/ipv6/udp.c --- linux-2.4.25.old/net/ipv6/udp.c 2004-02-18 13:36:32.000000000 +0000 +++ linux-2.4.25/net/ipv6/udp.c 2004-06-26 11:29:32.000000000 +0100 @@ -48,6 +48,7 @@ #include #include #include +#include #include @@ -232,6 +233,7 @@ int udpv6_connect(struct sock *sk, struc struct ip6_flowlabel *flowlabel = NULL; int addr_type; int err; + int reroute = 0; if (usin->sin6_family == AF_INET) { if (__ipv6_only_sock(sk)) @@ -331,7 +333,7 @@ ipv4_connected: fl.proto = IPPROTO_UDP; fl.fl6_dst = &np->daddr; - fl.fl6_src = &saddr; + fl.fl6_src = NULL; fl.oif = sk->bound_dev_if; fl.uli_u.ports.dport = sk->dport; fl.uli_u.ports.sport = sk->sport; @@ -348,29 +350,44 @@ ipv4_connected: struct rt0_hdr *rt0 = (struct rt0_hdr *) np->opt->srcrt; fl.fl6_dst = rt0->addr; } - dst = ip6_route_output(sk, &fl); - if ((err = dst->error) != 0) { dst_release(dst); fl6_sock_release(flowlabel); - return err; - } - - ip6_dst_store(sk, dst, fl.fl6_dst); - + return err; + } +#ifdef CONFIG_IPV6_SUBTREES + reroute = (fl.fl6_src == NULL); +#endif /* get the source adddress used in the apropriate device */ err = ipv6_get_saddr(dst, daddr, &saddr); + if (reroute) + dst_release(dst); + if (err == 0) { - if(ipv6_addr_any(&np->saddr)) +#ifdef CONFIG_IPV6_SUBTREES + if (reroute) { + fl.fl6_src = &saddr; + dst = ip6_route_output(sk, &fl); + if ((err = dst->error) != 0) { + dst_release(dst); + fl6_sock_release(flowlabel); + return err; + } + } +#endif + if(ipv6_addr_any(&np->saddr)) { ipv6_addr_copy(&np->saddr, &saddr); - + fl.fl6_src = &np->saddr; + } if(ipv6_addr_any(&np->rcv_saddr)) { ipv6_addr_copy(&np->rcv_saddr, &saddr); sk->rcv_saddr = LOOPBACK4_IPV6; } + ip6_dst_store(sk, dst, fl.fl6_dst, + fl.fl6_src == &np->saddr ? fl.fl6_src : NULL); sk->state = TCP_ESTABLISHED; } fl6_sock_release(flowlabel); @@ -894,6 +911,7 @@ static int udpv6_sendmsg(struct sock *sk opt = fl6_merge_options(&opt_space, flowlabel, opt); if (opt && opt->srcrt) udh.daddr = daddr; + udh.daddr = mipv6_get_fake_hdr_daddr(udh.daddr, daddr); udh.uh.source = sk->sport; udh.uh.len = len < 0x10000 ? htons(len) : 0; diff -uprN linux-2.4.25.old/net/netsyms.c linux-2.4.25/net/netsyms.c --- linux-2.4.25.old/net/netsyms.c 2003-11-28 18:26:21.000000000 +0000 +++ linux-2.4.25/net/netsyms.c 2004-06-26 11:29:32.000000000 +0100 @@ -190,6 +190,7 @@ EXPORT_SYMBOL(neigh_sysctl_register); #endif EXPORT_SYMBOL(pneigh_lookup); EXPORT_SYMBOL(pneigh_enqueue); +EXPORT_SYMBOL(pneigh_delete); EXPORT_SYMBOL(neigh_destroy); EXPORT_SYMBOL(neigh_parms_alloc); EXPORT_SYMBOL(neigh_parms_release);