diff options
Diffstat (limited to 'packages/linux/files/mipv6-1.1-v2.4.27.patch')
-rw-r--r-- | packages/linux/files/mipv6-1.1-v2.4.27.patch | 19736 |
1 files changed, 19736 insertions, 0 deletions
diff --git a/packages/linux/files/mipv6-1.1-v2.4.27.patch b/packages/linux/files/mipv6-1.1-v2.4.27.patch new file mode 100644 index 0000000000..b8fb071d28 --- /dev/null +++ b/packages/linux/files/mipv6-1.1-v2.4.27.patch @@ -0,0 +1,19736 @@ + +# +# Patch managed by http://www.holgerschurig.de/patcher.html +# + +--- linux-2.4.27/Documentation/Configure.help~mipv6-1.1-v2.4.26 ++++ linux-2.4.27/Documentation/Configure.help +@@ -6308,6 +6308,57 @@ + + 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 +--- linux-2.4.27/Documentation/DocBook/Makefile~mipv6-1.1-v2.4.26 ++++ linux-2.4.27/Documentation/DocBook/Makefile +@@ -2,7 +2,7 @@ + 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 libata.sgml ++ journal-api.sgml libata.sgml mip6-func.sgml + + PS := $(patsubst %.sgml, %.ps, $(BOOKS)) + PDF := $(patsubst %.sgml, %.pdf, $(BOOKS)) +@@ -96,6 +96,9 @@ + 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 \ +--- /dev/null ++++ linux-2.4.27/Documentation/DocBook/mip6-func.tmpl +@@ -0,0 +1,756 @@ ++<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook V3.1//EN"[]> ++<book id="LinuxMobileIPv6"> ++ <bookinfo> ++ <title>MIPL Mobile IPv6 Function Reference Guide</title> ++ ++ <authorgroup> ++ <author> ++ <othername>MIPL Mobile IPv6 for Linux Team</othername> ++ <affiliation> ++ <orgname>Helsinki University of Technology</orgname> ++ <orgdiv>Telecommunications Software and Multimedia Lab</orgdiv> ++ <address> ++ <pob>PO BOX 9201</pob> ++ <postcode>FIN-02015 HUT</postcode> ++ <country>Finland</country> ++ <email>mipl@list.mipl.mediapoli.com</email> ++ </address> ++ </affiliation> ++ </author> ++ </authorgroup> ++ ++ <copyright> ++ <year>2000-2001</year> ++ <holder>Helsinki University of Technology</holder> ++ </copyright> ++ ++ <legalnotice> ++ <para> ++ Copyright (c) 2001, 2002 MIPL Mobile IPv6 for Linux Team. ++ </para> ++ <para> ++ 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 <xref ++ linkend="gfdl">. ++ </para> ++ ++ </legalnotice> ++ </bookinfo> ++ ++<toc></toc> ++ ++ <preface id="intro"> ++ <title>Introduction</title> ++ ++ <para> ++ 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. ++ </para> ++ ++ <para> ++ MIPL is fully open source, licensed under the GNU General ++ Public License. Latest source for MIPL can be downloaded from ++ the MIPL website at: ++ </para> ++ <programlisting> ++ http://www.mipl.mediapoli.com/. ++ </programlisting> ++ <para> ++ Developers and users interested in MIPL can subscribe to the ++ MIPL mailing list by sending e-mail to ++ <email>majordomo@list.mipl.mediapoli.com</email> with ++ </para> ++ <programlisting> ++ subscribe mipl ++ </programlisting> ++ <para> ++ in the body of the message. ++ </para> ++ ++ <para> ++ 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. ++ </para> ++ ++ <para> ++ MIPL Mobile IPv6 for Linux Team members (past and present): ++ ++ <itemizedlist> ++ <listitem> ++ <address> ++ Sami Kivisaari <email>Sami.Kivisaari@hut.fi</email> ++ </address> ++ </listitem> ++ <listitem> ++ <address> ++ Niklas Kampe <email>Niklas.Kampe@hut.fi</email> ++ </address> ++ </listitem> ++ <listitem> ++ <address> ++ Juha Mynttinen <email>Juha.Mynttinen@hut.fi</email> ++ </address> ++ </listitem> ++ <listitem> ++ <address> ++ Toni Nykanen <email>Toni.Nykanen@iki.fi</email> ++ </address> ++ </listitem> ++ <listitem> ++ <address> ++ Henrik Petander <email>Henrik.Petander@hut.fi</email> ++ </address> ++ </listitem> ++ <listitem> ++ <address> ++ Antti Tuominen <email>ajtuomin@tml.hut.fi</email> ++ </address> ++ </listitem> ++ </itemizedlist> ++ ++ <itemizedlist> ++ <listitem> ++ <address> ++ Marko Myllynen ++ </address> ++ </listitem> ++ <listitem> ++ <address> ++ Ville Nuorvala <email>vnuorval@tcs.hut.fi</email> ++ </address> ++ </listitem> ++ <listitem> ++ <address> ++ Jaakko Laine <email>Jaakko.Laine@hut.fi</email> ++ </address> ++ </listitem> ++ </itemizedlist> ++ </para> ++ ++ </preface> ++ ++ <chapter id="common"> ++ <title>Common functions for all entities</title> ++ ++ <sect1><title>Low-level functions</title> ++ <para> ++ 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. ++ </para> ++!Inet/ipv6/mobile_ip6/mempool.h ++!Inet/ipv6/mobile_ip6/hashlist.h ++ </sect1> ++ ++ <sect1><title>Debug functions</title> ++ <para> ++ Debug and utility functions. These functions are available if ++ <constant>CONFIG_IPV6_MOBILITY_DEBUG</constant> is set. ++ Otherwise macros expand to no operation. ++ </para> ++!Inet/ipv6/mobile_ip6/debug.h ++!Inet/ipv6/mobile_ip6/mipv6.c ++ </sect1> ++ ++ <sect1><title>Extension Header functions</title> ++ <para> ++ These functions create and handle extension headers that are ++ specific to MIPv6. ++ </para> ++!Inet/ipv6/mobile_ip6/exthdrs.c ++ </sect1> ++ ++ <sect1><title>Mobility Header functions</title> ++ <para> ++ 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. ++ </para> ++!Inet/ipv6/mobile_ip6/sendopts.c ++!Inet/ipv6/mobile_ip6/mh_recv.c ++!Inet/ipv6/mobile_ip6/auth_subopt.c ++ </sect1> ++ ++ <sect1><title>Binding Cache</title> ++ <para> ++ All Mobile IPv6 entities have a binding cache. These functions ++ provide easy manipulation of the binding cache. ++ </para> ++!Inet/ipv6/mobile_ip6/bcache.c ++ </sect1> ++ ++ <sect1><title>Security</title> ++ ++ <para> ++ These functions are common authentication functions and ++ implement Draft 13 style IPSec AH support for Binding Updates. ++ </para> ++!Inet/ipv6/mobile_ip6/ah_algo.c ++!Inet/ipv6/mobile_ip6/sadb.c ++!Inet/ipv6/mobile_ip6/ah.c ++ </sect1> ++ ++ <sect1><title>Utility functions</title> ++ ++ <para> ++ These functions are general utility functions commonly used by ++ all entities. ++ </para> ++!Inet/ipv6/mobile_ip6/util.c ++ </sect1> ++ ++ </chapter> ++ ++ <chapter id="mn"> ++ <title>Mobile Node functions</title> ++ <sect1><title>General functions</title> ++ <para> ++ </para> ++!Inet/ipv6/mobile_ip6/mn.c ++ </sect1> ++ ++ <sect1><title>Binding Update List</title> ++ <para> ++ Mobile Node keeps track of sent binding updates in Binding ++ Update List. ++ </para> ++!Inet/ipv6/mobile_ip6/bul.c ++ </sect1> ++ ++ <sect1><title>Movement detection</title> ++ ++ <para> ++ These functions are used by the mobile node for movement ++ detection. ++ </para> ++!Inet/ipv6/mobile_ip6/mdetect.c ++ </sect1> ++ </chapter> ++ ++ <chapter id="ha"> ++ <title>Home Agent functions</title> ++ <sect1><title>General functions</title> ++ <para> ++ </para> ++!Inet/ipv6/mobile_ip6/ha.c ++ </sect1> ++ ++ <sect1><title>Duplicate Address Detection functions</title> ++ <para> ++ Home Agent does Duplicate Address Detection for Mobile Nodes' ++ addresses. These functions implement MIPv6 specific DAD ++ functionality. ++ </para> ++!Inet/ipv6/mobile_ip6/dad.c ++ </sect1> ++ ++ </chapter> ++ <appendix id="gfdl"> ++ <title>GNU Free Documentation License</title> ++ ++ <para> ++ Version 1.1, March 2000 ++ </para> ++ ++ <programlisting> ++ 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. ++ </programlisting> ++ ++ <sect1><title>0. PREAMBLE</title> ++ ++ <para> ++ 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. ++ </para> ++ ++ <para> ++ 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. ++ </para> ++ ++ <para> ++ 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. ++ </para> ++ ++ </sect1> ++ <sect1><title>1. APPLICABILITY AND DEFINITIONS</title> ++ ++ <para> ++ 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". ++ </para> ++ ++ <para> ++ 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. ++ </para> ++ ++ <para> ++ 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. ++ </para> ++ ++ <para> ++ 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. ++ </para> ++ ++ <para> ++ 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. ++ </para> ++ ++ <para> ++ 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". ++ </para> ++ ++ <para> ++ 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. ++ </para> ++ ++ <para> ++ 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. ++ </para> ++ ++ </sect1> ++ <sect1><title>2. VERBATIM COPYING</title> ++ ++ <para> ++ 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. ++ </para> ++ ++ <para> ++ You may also lend copies, under the same conditions stated ++ above, and you may publicly display copies. ++ </para> ++ ++ </sect1> ++ <sect1><title>3. COPYING IN QUANTITY</title> ++ ++ <para> ++ 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. ++ </para> ++ ++ <para> ++ 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. ++ </para> ++ ++ <para> ++ 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. ++ </para> ++ ++ <para> ++ 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. ++ </para> ++ ++ </sect1> ++ <sect1><title>4. MODIFICATIONS</title> ++ ++ <para> ++ 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: ++ </para> ++ ++ <para> ++ <itemizedlist spacing=compact> ++ <listitem> ++ <para> ++ 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. ++ </para> ++ </listitem> ++ <listitem> ++ <para> ++ 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). ++ </para> ++ </listitem> ++ <listitem> ++ <para> ++ C. State on the Title page the name of the publisher of the ++ Modified Version, as the publisher. ++ </para> ++ </listitem> ++ <listitem> ++ <para> ++ D. Preserve all the copyright notices of the Document. ++ </para> ++ </listitem> ++ <listitem> ++ <para> ++ E. Add an appropriate copyright notice for your ++ modifications adjacent to the other copyright notices. ++ </para> ++ </listitem> ++ <listitem> ++ <para> ++ 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. ++ </para> ++ </listitem> ++ <listitem> ++ <para> ++ G. Preserve in that license notice the full lists of ++ Invariant Sections and required Cover Texts given in the ++ Document's license notice. ++ </para> ++ </listitem> ++ <listitem> ++ <para> ++ H. Include an unaltered copy of this License. ++ </para> ++ </listitem> ++ <listitem> ++ <para> ++ 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. ++ </para> ++ </listitem> ++ <listitem> ++ <para> ++ 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. ++ </para> ++ </listitem> ++ <listitem> ++ <para> ++ 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. ++ </para> ++ </listitem> ++ <listitem> ++ <para> ++ 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. ++ </para> ++ </listitem> ++ <listitem> ++ <para> ++ M. Delete any section entitled "Endorsements". Such a ++ section may not be included in the Modified Version. ++ </para> ++ </listitem> ++ <listitem> ++ <para> ++ N. Do not retitle any existing section as "Endorsements" or ++ to conflict in title with any Invariant Section. ++ </para> ++ </listitem> ++ </itemizedlist> ++ </para> ++ ++ <para> ++ 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. ++ </para> ++ ++ <para> ++ 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. ++ </para> ++ ++ <para> ++ 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. ++ </para> ++ ++ <para> ++ 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. ++ </para> ++ ++ </sect1> ++ <sect1><title>5. COMBINING DOCUMENTS</title> ++ ++ <para> ++ 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. ++ </para> ++ ++ <para> ++ 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. ++ </para> ++ ++ <para> ++ 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." ++ </para> ++ ++ </sect1> ++ <sect1><title>6. COLLECTIONS OF DOCUMENTS</title> ++ ++ <para> ++ 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. ++ </para> ++ ++ <para> ++ 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. ++ </para> ++ ++ </sect1> ++ <sect1><title>7. AGGREGATION WITH INDEPENDENT WORKS</title> ++ ++ <para> ++ 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. ++ </para> ++ ++ <para> ++ 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. ++ </para> ++ ++ </sect1> ++ <sect1><title>8. TRANSLATION</title> ++ ++ <para> ++ 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. ++ </para> ++ ++ </sect1> ++ <sect1><title>9. TERMINATION</title> ++ ++ <para> ++ 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. ++ </para> ++ ++ </sect1> ++ <sect1><title>10. FUTURE REVISIONS OF THIS LICENSE</title> ++ ++ <para> ++ 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/. ++ </para> ++ ++ <para> ++ 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. ++ </para> ++ ++ </sect1> ++ </appendix> ++</book> +--- linux-2.4.27/include/linux/icmpv6.h~mipv6-1.1-v2.4.26 ++++ linux-2.4.27/include/linux/icmpv6.h +@@ -40,14 +40,16 @@ + 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 <asm/byteorder.h>" + #endif +@@ -70,6 +72,7 @@ + #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 + }; + + +--- linux-2.4.27/include/linux/if_arp.h~mipv6-1.1-v2.4.26 ++++ linux-2.4.27/include/linux/if_arp.h +@@ -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 */ +--- linux-2.4.27/include/linux/in6.h~mipv6-1.1-v2.4.26 ++++ linux-2.4.27/include/linux/in6.h +@@ -142,6 +142,11 @@ + #define IPV6_TLV_JUMBO 194 + + /* ++ * Mobile IPv6 TLV options. ++ */ ++#define MIPV6_TLV_HOMEADDR 201 ++ ++/* + * IPV6 socket options + */ + +--- linux-2.4.27/include/linux/ipv6.h~mipv6-1.1-v2.4.26 ++++ linux-2.4.27/include/linux/ipv6.h +@@ -29,6 +29,7 @@ + + #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 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 @@ + 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 +--- linux-2.4.27/include/linux/ipv6_route.h~mipv6-1.1-v2.4.26 ++++ linux-2.4.27/include/linux/ipv6_route.h +@@ -33,6 +33,7 @@ + #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 + +--- /dev/null ++++ linux-2.4.27/include/linux/ipv6_tunnel.h +@@ -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 +--- linux-2.4.27/include/linux/rtnetlink.h~mipv6-1.1-v2.4.26 ++++ linux-2.4.27/include/linux/rtnetlink.h +@@ -315,6 +315,7 @@ + IFA_BROADCAST, + IFA_ANYCAST, + IFA_CACHEINFO, ++ IFA_HOMEAGENT, + __IFA_MAX + }; + +@@ -324,6 +325,7 @@ + + #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 +--- linux-2.4.27/include/linux/skbuff.h~mipv6-1.1-v2.4.26 ++++ linux-2.4.27/include/linux/skbuff.h +@@ -177,7 +177,7 @@ + * 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; +--- linux-2.4.27/include/linux/sysctl.h~mipv6-1.1-v2.4.26 ++++ linux-2.4.27/include/linux/sysctl.h +@@ -404,6 +404,23 @@ + NET_IPV6_ICMP=19, + NET_IPV6_BINDV6ONLY=20, + NET_IPV6_MLD_MAX_MSF=25, ++ 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 { +--- linux-2.4.27/include/net/addrconf.h~mipv6-1.1-v2.4.26 ++++ linux-2.4.27/include/net/addrconf.h +@@ -16,9 +16,11 @@ + #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 @@ + 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_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 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) + { +--- linux-2.4.27/include/net/ip6_route.h~mipv6-1.1-v2.4.26 ++++ linux-2.4.27/include/net/ip6_route.h +@@ -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 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 @@ + */ + + 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 @@ + 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); + } +--- linux-2.4.27/include/net/ipv6.h~mipv6-1.1-v2.4.26 ++++ linux-2.4.27/include/net/ipv6.h +@@ -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 + +@@ -146,9 +147,12 @@ + __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; + +@@ -257,6 +261,38 @@ + 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 + */ +--- /dev/null ++++ linux-2.4.27/include/net/ipv6_tunnel.h +@@ -0,0 +1,92 @@ ++/* ++ * $Id$ ++ */ ++ ++#ifndef _NET_IPV6_TUNNEL_H ++#define _NET_IPV6_TUNNEL_H ++ ++#include <linux/ipv6.h> ++#include <linux/netdevice.h> ++#include <linux/ipv6_tunnel.h> ++#include <linux/skbuff.h> ++#include <asm/atomic.h> ++ ++/* 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 +--- /dev/null ++++ linux-2.4.27/include/net/mipglue.h +@@ -0,0 +1,266 @@ ++/* ++ * Glue for Mobility support integration to IPv6 ++ * ++ * Authors: ++ * Antti Tuominen <ajtuomin@cc.hut.fi> ++ * ++ * $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 <net/mipv6.h> ++#include <linux/slab.h> ++#include <net/ipv6.h> ++ ++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 */ +--- /dev/null ++++ linux-2.4.27/include/net/mipv6.h +@@ -0,0 +1,258 @@ ++/* ++ * Mobile IPv6 header-file ++ * ++ * Authors: ++ * Sami Kivisaari <skivisaa@cc.hut.fi> ++ * ++ * $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 <linux/types.h> ++#include <asm/byteorder.h> ++#include <linux/in6.h> ++ ++/* ++ * ++ * 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 */ +--- linux-2.4.27/include/net/ndisc.h~mipv6-1.1-v2.4.26 ++++ linux-2.4.27/include/net/ndisc.h +@@ -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 @@ + } __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 @@ + #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 @@ + 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); +--- linux-2.4.27/include/net/sock.h~mipv6-1.1-v2.4.26 ++++ linux-2.4.27/include/net/sock.h +@@ -149,7 +149,9 @@ + 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; +--- linux-2.4.27/net/Makefile~mipv6-1.1-v2.4.26 ++++ linux-2.4.27/net/Makefile +@@ -7,7 +7,7 @@ + + O_TARGET := network.o + +-mod-subdirs := ipv4/netfilter ipv6/netfilter ipx irda bluetooth atm netlink sched core sctp 802 ++mod-subdirs := ipv4/netfilter ipv6/netfilter ipx irda bluetooth atm netlink sched core sctp 802 ipv6 + export-objs := netsyms.o + + subdir-y := core ethernet +@@ -25,6 +25,7 @@ + ifneq ($(CONFIG_IPV6),n) + ifneq ($(CONFIG_IPV6),) + subdir-$(CONFIG_NETFILTER) += ipv6/netfilter ++subdir-$(CONFIG_IPV6_MOBILITY) += ipv6/mobile_ip6 + endif + endif + +--- linux-2.4.27/net/core/neighbour.c~mipv6-1.1-v2.4.26 ++++ linux-2.4.27/net/core/neighbour.c +@@ -386,7 +386,7 @@ + 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; + +--- linux-2.4.27/net/ipv6/Config.in~mipv6-1.1-v2.4.26 ++++ linux-2.4.27/net/ipv6/Config.in +@@ -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 +--- linux-2.4.27/net/ipv6/Makefile~mipv6-1.1-v2.4.26 ++++ linux-2.4.27/net/ipv6/Makefile +@@ -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 + +-export-objs := ipv6_syms.o ++ifneq ($(CONFIG_IPV6_MOBILITY),n) ++ifneq ($(CONFIG_IPV6_MOBILITY),) ++ipv6-objs += mipglue.o ++endif ++endif ++ ++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 + +--- linux-2.4.27/net/ipv6/addrconf.c~mipv6-1.1-v2.4.26 ++++ linux-2.4.27/net/ipv6/addrconf.c +@@ -68,6 +68,8 @@ + + #include <asm/uaccess.h> + ++#include <net/mipglue.h> ++ + #define IPV6_MAX_ADDRESSES 16 + + /* Set to 3 to get tracing... */ +@@ -103,9 +105,9 @@ + + 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 @@ + 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 @@ + + /* 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 @@ + 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 @@ + } + + +-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 @@ + 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 @@ + 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 @@ + 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 @@ + /* + * 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 @@ + 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 @@ + */ + + 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 @@ + 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; + +--- linux-2.4.27/net/ipv6/af_inet6.c~mipv6-1.1-v2.4.26 ++++ linux-2.4.27/net/ipv6/af_inet6.c +@@ -58,6 +58,9 @@ + #include <net/transp_v6.h> + #include <net/ip6_route.h> + #include <net/addrconf.h> ++#ifdef CONFIG_IPV6_TUNNEL ++#include <net/ipv6_tunnel.h> ++#endif + + #include <asm/uaccess.h> + #include <asm/system.h> +@@ -646,6 +649,11 @@ + 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 @@ + #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 @@ + ip6_route_cleanup(); + ipv6_packet_cleanup(); + igmp6_cleanup(); ++#ifdef CONFIG_IPV6_TUNNEL ++ ip6_tunnel_cleanup(); ++#endif + ndisc_cleanup(); + icmpv6_cleanup(); + #ifdef CONFIG_SYSCTL +--- linux-2.4.27/net/ipv6/exthdrs.c~mipv6-1.1-v2.4.26 ++++ linux-2.4.27/net/ipv6/exthdrs.c +@@ -41,6 +41,9 @@ + #include <net/ip6_route.h> + #include <net/addrconf.h> + ++#include <net/mipglue.h> ++#include <net/mipv6.h> ++ + #include <asm/uaccess.h> + + /* +@@ -160,7 +163,8 @@ + *****************************/ + + 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 @@ + + 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 @@ + 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 @@ + + 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 @@ + 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 @@ + 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 @@ + 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 @@ + 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 @@ + *((char**)&opt2->auth) += dif; + if (opt2->srcrt) + *((char**)&opt2->srcrt) += dif; ++ if (opt2->srcrt2) ++ *((char**)&opt2->srcrt2) += dif; + } + return opt2; + } +--- linux-2.4.27/net/ipv6/icmp.c~mipv6-1.1-v2.4.26 ++++ linux-2.4.27/net/ipv6/icmp.c +@@ -61,6 +61,8 @@ + #include <net/addrconf.h> + #include <net/icmp.h> + ++#include <net/mipglue.h> ++ + #include <asm/uaccess.h> + #include <asm/system.h> + +@@ -364,6 +366,8 @@ + + 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 @@ + 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; + +@@ -598,6 +602,13 @@ + case ICMPV6_MLD2_REPORT: + 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"); +--- linux-2.4.27/net/ipv6/ip6_fib.c~mipv6-1.1-v2.4.26 ++++ linux-2.4.27/net/ipv6/ip6_fib.c +@@ -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 <linux/config.h> + #include <linux/errno.h> +@@ -40,7 +41,6 @@ + #include <net/ip6_route.h> + + #define RT6_DEBUG 2 +-#undef CONFIG_IPV6_SUBTREES + + #if RT6_DEBUG >= 3 + #define RT6_TRACE(x...) printk(KERN_DEBUG x) +@@ -500,6 +500,8 @@ + 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. + * <destination addr>/<source addr> +@@ -508,17 +510,19 @@ + + 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 @@ + + /* 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 @@ + /* 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 @@ + } + + 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 @@ + 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 @@ + 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 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 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 @@ + } + 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(); + } + +--- linux-2.4.27/net/ipv6/ip6_input.c~mipv6-1.1-v2.4.26 ++++ linux-2.4.27/net/ipv6/ip6_input.c +@@ -40,13 +40,42 @@ + #include <net/ip6_route.h> + #include <net/addrconf.h> + ++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); + } + +--- linux-2.4.27/net/ipv6/ip6_output.c~mipv6-1.1-v2.4.26 ++++ linux-2.4.27/net/ipv6/ip6_output.c +@@ -50,6 +50,8 @@ + #include <net/rawv6.h> + #include <net/icmp.h> + ++#include <net/mipglue.h> ++ + static __inline__ void ipv6_select_ident(struct sk_buff *skb, struct frag_hdr *fhdr) + { + static u32 ipv6_fragmentation_id = 1; +@@ -194,7 +196,14 @@ + 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 @@ + 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 @@ + + 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 @@ + 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 @@ + + 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 @@ + 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 @@ + 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 @@ + 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 @@ + 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 @@ + + 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 @@ + * 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 @@ + 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. + */ +--- linux-2.4.27/net/ipv6/ipv6_syms.c~mipv6-1.1-v2.4.26 ++++ linux-2.4.27/net/ipv6/ipv6_syms.c +@@ -6,6 +6,8 @@ + #include <net/ipv6.h> + #include <net/addrconf.h> + #include <net/ip6_route.h> ++#include <net/ndisc.h> ++#include <net/mipglue.h> + + EXPORT_SYMBOL(ipv6_addr_type); + EXPORT_SYMBOL(icmpv6_send); +@@ -35,3 +37,47 @@ + EXPORT_SYMBOL(in6_dev_finish_destroy); + EXPORT_SYMBOL(ipv6_skip_exthdr); + ++#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 +--- /dev/null ++++ linux-2.4.27/net/ipv6/ipv6_tunnel.c +@@ -0,0 +1,1604 @@ ++/* ++ * IPv6 over IPv6 tunnel device ++ * Linux INET6 implementation ++ * ++ * Authors: ++ * Ville Nuorvala <vnuorval@tcs.hut.fi> ++ * ++ * $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 <linux/config.h> ++#include <linux/module.h> ++#include <linux/errno.h> ++#include <linux/types.h> ++#include <linux/socket.h> ++#include <linux/sockios.h> ++#include <linux/if.h> ++#include <linux/in.h> ++#include <linux/ip.h> ++#include <linux/if_tunnel.h> ++#include <linux/net.h> ++#include <linux/in6.h> ++#include <linux/netdevice.h> ++#include <linux/if_arp.h> ++#include <linux/icmpv6.h> ++#include <linux/init.h> ++#include <linux/route.h> ++#include <linux/rtnetlink.h> ++#include <linux/tqueue.h> ++ ++#include <asm/uaccess.h> ++#include <asm/atomic.h> ++ ++#include <net/sock.h> ++#include <net/ipv6.h> ++#include <net/protocol.h> ++#include <net/ip6_route.h> ++#include <net/addrconf.h> ++#include <net/ipv6_tunnel.h> ++ ++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 ++ +--- /dev/null ++++ linux-2.4.27/net/ipv6/mipglue.c +@@ -0,0 +1,63 @@ ++/* ++ * Glue for Mobility support integration to IPv6 ++ * ++ * Authors: ++ * Antti Tuominen <ajtuomin@cc.hut.fi> ++ * ++ * $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 <linux/sched.h> ++ ++#include <net/ipv6.h> ++#include <net/addrconf.h> ++#include <net/neighbour.h> ++#include <net/mipglue.h> ++ ++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; ++} ++ +--- /dev/null ++++ linux-2.4.27/net/ipv6/mobile_ip6/Config.in +@@ -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 +--- /dev/null ++++ linux-2.4.27/net/ipv6/mobile_ip6/Makefile +@@ -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) +--- /dev/null ++++ linux-2.4.27/net/ipv6/mobile_ip6/README +@@ -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 +--- /dev/null ++++ linux-2.4.27/net/ipv6/mobile_ip6/auth_opt.c +@@ -0,0 +1,121 @@ ++/* ++ * MIPv6 Binding Authentication Data Option functions ++ * ++ * Authors: ++ * Henrik Petander <lpetande@tml.hut.fi> ++ * ++ * $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 <linux/autoconf.h> ++#include <linux/icmpv6.h> ++#include <net/mipv6.h> ++ ++#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; ++} +--- /dev/null ++++ linux-2.4.27/net/ipv6/mobile_ip6/bcache.c +@@ -0,0 +1,746 @@ ++/* ++ * Binding Cache ++ * ++ * Authors: ++ * Juha Mynttinen <jmynttin@cc.hut.fi> ++ * ++ * $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 <linux/autoconf.h> ++#include <linux/sched.h> ++#include <linux/timer.h> ++#include <linux/in6.h> ++#include <linux/init.h> ++#include <linux/spinlock.h> ++#include <linux/proc_fs.h> ++#include <linux/ipv6_route.h> ++#include <net/ipv6.h> ++#include <net/addrconf.h> ++#include <net/tcp.h> ++#include <net/udp.h> ++#include <net/ip6_route.h> ++#include <net/mipv6.h> ++ ++#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; ++} +--- /dev/null ++++ linux-2.4.27/net/ipv6/mobile_ip6/bcache.h +@@ -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 <linux/in6.h> ++#include <linux/timer.h> ++#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 */ +--- /dev/null ++++ linux-2.4.27/net/ipv6/mobile_ip6/bul.c +@@ -0,0 +1,634 @@ ++/* ++ * Binding update list ++ * ++ * Authors: ++ * Juha Mynttinen <jmynttin@cc.hut.fi> ++ * ++ * $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 <linux/autoconf.h> ++#include <linux/sched.h> ++#include <linux/timer.h> ++#include <linux/in6.h> ++#include <linux/init.h> ++#include <linux/spinlock.h> ++#include <net/ipv6.h> ++#include <net/mipv6.h> ++#include <linux/proc_fs.h> ++ ++#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"); ++} +--- /dev/null ++++ linux-2.4.27/net/ipv6/mobile_ip6/bul.h +@@ -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 */ +--- /dev/null ++++ linux-2.4.27/net/ipv6/mobile_ip6/config.h +@@ -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; +--- /dev/null ++++ linux-2.4.27/net/ipv6/mobile_ip6/debug.h +@@ -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 <linux/autoconf.h> ++ ++/* 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<len; i++) { \ ++ if (i%16 == 0) printk("\n%04x: ", i); \ ++ printk("%02x ", ((unsigned char *)data)[i]); \ ++ } \ ++ printk("\n\n"); \ ++ } \ ++} ++ ++#define DEBUG(x,y,z...) debug_print(x,__FUNCTION__,y,##z) ++#define DEBUG_FUNC() \ ++DEBUG(DBG_FUNC_ENTRY, "%s(%d)/%s: ", __FILE__,__LINE__,__FUNCTION__) ++ ++#else ++#define DEBUG(x,y,z...) ++#define DEBUG_FUNC() ++#define debug_print_buffer(x,y,z) ++#endif ++ ++#undef ASSERT ++#define ASSERT(expression) { \ ++ if (!(expression)) { \ ++ (void)printk(KERN_ERR \ ++ "Assertion \"%s\" failed: file \"%s\", function \"%s\", line %d\n", \ ++ #expression, __FILE__, __FUNCTION__, __LINE__); \ ++ BUG(); \ ++ } \ ++} ++ ++#endif /* _DEBUG_H */ +--- /dev/null ++++ linux-2.4.27/net/ipv6/mobile_ip6/exthdrs.c +@@ -0,0 +1,394 @@ ++/* ++ * Extension Header handling and adding code ++ * ++ * Authors: ++ * Sami Kivisaari <skivisaa@cc.hut.fi> ++ * ++ * $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 <linux/types.h> ++#include <linux/slab.h> ++ ++#include <net/ipv6.h> ++#include <net/ip6_route.h> ++#include <net/addrconf.h> ++#include <net/mipv6.h> ++ ++#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; ++} +--- /dev/null ++++ linux-2.4.27/net/ipv6/mobile_ip6/exthdrs.h +@@ -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 */ +--- /dev/null ++++ linux-2.4.27/net/ipv6/mobile_ip6/ha.c +@@ -0,0 +1,553 @@ ++/* ++ * Home-agent functionality ++ * ++ * Authors: ++ * Sami Kivisaari <skivisaa@cc.hut.fi> ++ * Henrik Petander <lpetande@cc.hut.fi> ++ * ++ * $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 <linux/autoconf.h> ++#include <linux/net.h> ++#include <linux/skbuff.h> ++#include <linux/if_ether.h> ++#include <linux/netdevice.h> ++#include <linux/in6.h> ++#include <linux/init.h> ++#include <linux/netfilter.h> ++#include <linux/netfilter_ipv6.h> ++#ifdef CONFIG_SYSCTL ++#include <linux/sysctl.h> ++#endif ++ ++#include <net/neighbour.h> ++#include <net/ipv6.h> ++#include <net/ip6_fib.h> ++#include <net/ip6_route.h> ++#include <net/ndisc.h> ++#include <net/addrconf.h> ++#include <net/neighbour.h> ++ ++#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; ++} +--- /dev/null ++++ linux-2.4.27/net/ipv6/mobile_ip6/ha.h +@@ -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 +--- /dev/null ++++ linux-2.4.27/net/ipv6/mobile_ip6/halist.c +@@ -0,0 +1,507 @@ ++/* ++ * Home Agents List ++ * ++ * Authors: ++ * Antti Tuominen <ajtuomin@tml.hut.fi> ++ * ++ * $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 <linux/autoconf.h> ++#include <linux/sched.h> ++#include <linux/timer.h> ++#include <linux/proc_fs.h> ++#include <linux/init.h> ++#include <net/ipv6.h> ++#include <net/addrconf.h> ++ ++#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); ++} +--- /dev/null ++++ linux-2.4.27/net/ipv6/mobile_ip6/halist.h +@@ -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 */ +--- /dev/null ++++ linux-2.4.27/net/ipv6/mobile_ip6/hashlist.c +@@ -0,0 +1,351 @@ ++/* ++ * Generic hashtable with chaining. Supports secodary sort order ++ * with doubly linked-list. ++ * ++ * Authors: ++ * Sami Kivisaari <skivisaa@cc.hut.fi> ++ * Antti Tuominen <ajtuomin@tml.hut.fi> ++ * ++ * $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 <linux/slab.h> ++#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); ++} +--- /dev/null ++++ linux-2.4.27/net/ipv6/mobile_ip6/hashlist.h +@@ -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 +--- /dev/null ++++ linux-2.4.27/net/ipv6/mobile_ip6/hmac.c +@@ -0,0 +1,658 @@ ++/* Authentication algorithms ++ * ++ * Authors: ++ * Alexis Olivereau <Alexis.Olivereau@crm.mot.com> ++ * ++ * $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 <linux/sched.h> ++#include <linux/tty.h> ++#include <linux/types.h> ++#include <linux/slab.h> ++#include <linux/in6.h> ++ ++#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 */ ++} +--- /dev/null ++++ linux-2.4.27/net/ipv6/mobile_ip6/hmac.h +@@ -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 <linux/types.h> ++#include <linux/in6.h> ++ ++#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 */ +--- /dev/null ++++ linux-2.4.27/net/ipv6/mobile_ip6/ioctl_mn.c +@@ -0,0 +1,142 @@ ++/* ++ * Mobile Node IOCTL Control device ++ * ++ * Authors: ++ * Henrik Petander <lpetande@tml.hut.fi> ++ * ++ * $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 <linux/config.h> ++#include <linux/module.h> ++#include <linux/fs.h> ++#include <linux/poll.h> ++#include <linux/ioctl.h> ++#include <net/ipv6.h> ++#include <asm/uaccess.h> ++ ++#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); ++} +--- /dev/null ++++ linux-2.4.27/net/ipv6/mobile_ip6/mdetect.c +@@ -0,0 +1,1153 @@ ++/* ++ * Movement Detection Module ++ * ++ * Authors: ++ * Henrik Petander <lpetande@cc.hut.fi> ++ * ++ * $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 <linux/autoconf.h> ++#include <linux/errno.h> ++#include <linux/init.h> ++#include <linux/if_arp.h> ++#include <linux/route.h> ++#include <net/ipv6.h> ++#include <net/ip6_route.h> ++#include <net/addrconf.h> ++#include <net/mipglue.h> ++#ifdef CONFIG_SYSCTL ++#include <linux/sysctl.h> ++#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; ++} +--- /dev/null ++++ linux-2.4.27/net/ipv6/mobile_ip6/mdetect.h +@@ -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 */ +--- /dev/null ++++ linux-2.4.27/net/ipv6/mobile_ip6/mipv6_icmp.c +@@ -0,0 +1,342 @@ ++/** ++ * Generic icmp routines ++ * ++ * Authors: ++ * Jaakko Laine <medved@iki.fi>, ++ * Ville Nuorvala <vnuorval@tcs.hut.fi> ++ * ++ * $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 <linux/config.h> ++#include <linux/icmpv6.h> ++#include <net/checksum.h> ++#include <net/ipv6.h> ++#include <net/ip6_route.h> ++#include <net/mipv6.h> ++#include <net/mipglue.h> ++ ++#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 */ ++} +--- /dev/null ++++ linux-2.4.27/net/ipv6/mobile_ip6/mipv6_icmp.h +@@ -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 <linux/config.h> ++#include <linux/in6.h> ++ ++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 +--- /dev/null ++++ linux-2.4.27/net/ipv6/mobile_ip6/mipv6_icmp_ha.c +@@ -0,0 +1,158 @@ ++/* ++ * Home Agent specific ICMP routines ++ * ++ * Authors: ++ * Antti Tuominen <ajtuomin@tml.hut.fi> ++ * Jaakko Laine <medved@iki.fi> ++ * ++ * $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 <linux/autoconf.h> ++#include <linux/sched.h> ++#include <net/ipv6.h> ++#include <net/addrconf.h> ++#include <net/ip6_route.h> ++#include <net/mipv6.h> ++ ++#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 +--- /dev/null ++++ linux-2.4.27/net/ipv6/mobile_ip6/mipv6_icmp_mn.c +@@ -0,0 +1,273 @@ ++/* ++ * Mobile Node specific ICMP routines ++ * ++ * Authors: ++ * Antti Tuominen <ajtuomin@tml.hut.fi> ++ * Jaakko Laine <medved@iki.fi> ++ * ++ * $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 <linux/sched.h> ++#include <net/ipv6.h> ++#include <net/ip6_route.h> ++#include <net/addrconf.h> ++#include <net/mipv6.h> ++ ++#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 +--- /dev/null ++++ linux-2.4.27/net/ipv6/mobile_ip6/mn.c +@@ -0,0 +1,1521 @@ ++/* ++ * Mobile-node functionality ++ * ++ * Authors: ++ * Sami Kivisaari <skivisaa@cc.hut.fi> ++ * ++ * $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 <linux/autoconf.h> ++#include <linux/sched.h> ++#include <linux/ipv6.h> ++#include <linux/net.h> ++#include <linux/init.h> ++#include <linux/skbuff.h> ++#include <linux/rtnetlink.h> ++#include <linux/if_arp.h> ++#include <linux/ipsec.h> ++#include <linux/notifier.h> ++#include <linux/list.h> ++#include <linux/route.h> ++#include <linux/netfilter.h> ++#include <linux/netfilter_ipv6.h> ++#include <linux/tqueue.h> ++#include <linux/proc_fs.h> ++ ++#include <asm/uaccess.h> ++ ++#include <net/ipv6.h> ++#include <net/addrconf.h> ++#include <net/neighbour.h> ++#include <net/ndisc.h> ++#include <net/ip6_route.h> ++#include <net/mipglue.h> ++ ++#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(); ++} +--- /dev/null ++++ linux-2.4.27/net/ipv6/mobile_ip6/mn.h +@@ -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 <linux/in6.h> ++ ++/* 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 */ +--- /dev/null ++++ linux-2.4.27/net/ipv6/mobile_ip6/mobhdr.h +@@ -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 <net/mipv6.h> ++ ++/* 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 */ +--- /dev/null ++++ linux-2.4.27/net/ipv6/mobile_ip6/mobhdr_common.c +@@ -0,0 +1,1210 @@ ++/* ++ * Mobile IPv6 Mobility Header Common Functions ++ * ++ * Authors: ++ * Antti Tuominen <ajtuomin@tml.hut.fi> ++ * ++ * $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 <linux/autoconf.h> ++#include <linux/types.h> ++#include <linux/in6.h> ++#include <linux/skbuff.h> ++#include <linux/ipsec.h> ++#include <linux/init.h> ++#include <net/ipv6.h> ++#include <net/ip6_route.h> ++#include <net/addrconf.h> ++#include <net/mipv6.h> ++#include <net/checksum.h> ++#include <net/protocol.h> ++ ++#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)); ++} +--- /dev/null ++++ linux-2.4.27/net/ipv6/mobile_ip6/mobhdr_mn.c +@@ -0,0 +1,1155 @@ ++/* ++ * Mobile IPv6 Mobility Header Functions for Mobile Node ++ * ++ * Authors: ++ * Antti Tuominen <ajtuomin@tml.hut.fi> ++ * Niklas Kämpe <nhkampe@cc.hut.fi> ++ * Henrik Petander <henrik.petander@hut.fi> ++ * ++ * $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 <linux/types.h> ++#include <linux/sched.h> ++#include <linux/init.h> ++#include <net/ipv6.h> ++#include <net/addrconf.h> ++#include <net/mipv6.h> ++ ++#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); ++} +--- /dev/null ++++ linux-2.4.27/net/ipv6/mobile_ip6/module_cn.c +@@ -0,0 +1,167 @@ ++/* ++ * Mobile IPv6 Common Module ++ * ++ * Authors: ++ * Sami Kivisaari <skivisaa@cc.hut.fi> ++ * Antti Tuominen <ajtuomin@tml.hut.fi> ++ * ++ * $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 <linux/config.h> ++#include <linux/module.h> ++#include <linux/init.h> ++ ++#ifdef CONFIG_SYSCTL ++#include <linux/sysctl.h> ++#endif /* CONFIG_SYSCTL */ ++ ++#include <net/mipglue.h> ++ ++#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 */ +--- /dev/null ++++ linux-2.4.27/net/ipv6/mobile_ip6/module_ha.c +@@ -0,0 +1,264 @@ ++/* ++ * Mobile IPv6 Home Agent Module ++ * ++ * Authors: ++ * Sami Kivisaari <skivisaa@cc.hut.fi> ++ * Antti Tuominen <ajtuomin@tml.hut.fi> ++ * ++ * $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 <linux/config.h> ++#include <linux/module.h> ++#include <linux/init.h> ++ ++#ifdef CONFIG_SYSCTL ++#include <linux/sysctl.h> ++#endif /* CONFIG_SYSCTL */ ++ ++#include <net/mipglue.h> ++#include <net/addrconf.h> ++ ++#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 */ +--- /dev/null ++++ linux-2.4.27/net/ipv6/mobile_ip6/module_mn.c +@@ -0,0 +1,188 @@ ++/* ++ * Mobile IPv6 Mobile Node Module ++ * ++ * Authors: ++ * Sami Kivisaari <skivisaa@cc.hut.fi> ++ * Antti Tuominen <ajtuomin@tml.hut.fi> ++ * ++ * $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 <linux/config.h> ++#include <linux/module.h> ++#include <linux/init.h> ++ ++#ifdef CONFIG_SYSCTL ++#include <linux/sysctl.h> ++#endif /* CONFIG_SYSCTL */ ++ ++#include <net/mipglue.h> ++ ++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 */ +--- /dev/null ++++ linux-2.4.27/net/ipv6/mobile_ip6/multiaccess_ctl.c +@@ -0,0 +1,287 @@ ++/* ++ * 2001 (c) Oy L M Ericsson Ab ++ * ++ * Author: NomadicLab / Ericsson Research <ipv6@nomadiclab.com> ++ * ++ * $Id$ ++ * ++ */ ++ ++/* ++ * Vertical hand-off information manager ++ */ ++ ++#include <linux/netdevice.h> ++#include <linux/in6.h> ++#include <linux/module.h> ++#include <linux/init.h> ++#include <linux/proc_fs.h> ++#include <linux/string.h> ++#include <linux/kernel.h> ++#include <asm/io.h> ++#include <asm/uaccess.h> ++#include <linux/list.h> ++#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"); ++} +--- /dev/null ++++ linux-2.4.27/net/ipv6/mobile_ip6/multiaccess_ctl.h +@@ -0,0 +1,77 @@ ++/* ++ * 2001 (c) Oy L M Ericsson Ab ++ * ++ * Author: NomadicLab / Ericsson Research <ipv6@nomadiclab.com> ++ * ++ * $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 +--- /dev/null ++++ linux-2.4.27/net/ipv6/mobile_ip6/ndisc_ha.c +@@ -0,0 +1,596 @@ ++/* ++ * Mobile IPv6 Duplicate Address Detection Functions ++ * ++ * Authors: ++ * Krishna Kumar <krkumar@us.ibm.com> ++ * ++ * $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 <linux/autoconf.h> ++#include <linux/types.h> ++#include <linux/init.h> ++#include <linux/skbuff.h> ++#include <linux/in6.h> ++#include <net/ipv6.h> ++#include <net/addrconf.h> ++#include <net/mipv6.h> ++ ++#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); ++} +--- /dev/null ++++ linux-2.4.27/net/ipv6/mobile_ip6/prefix.c +@@ -0,0 +1,217 @@ ++/** ++ * Prefix solicitation and advertisement ++ * ++ * Authors: ++ * Jaakko Laine <medved@iki.fi> ++ * ++ * $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 <linux/config.h> ++#include <linux/icmpv6.h> ++#include <linux/net.h> ++#include <linux/spinlock.h> ++#include <linux/timer.h> ++#include <linux/netdevice.h> ++#include <net/ipv6.h> ++#include <net/addrconf.h> ++#include <net/ip6_route.h> ++#include <net/mipv6.h> ++ ++#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); ++} +--- /dev/null ++++ linux-2.4.27/net/ipv6/mobile_ip6/prefix.h +@@ -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 <net/addrconf.h> ++ ++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 +--- /dev/null ++++ linux-2.4.27/net/ipv6/mobile_ip6/prefix_ha.c +@@ -0,0 +1,122 @@ ++/** ++ * Prefix advertisement for Home Agent ++ * ++ * Authors: ++ * Jaakko Laine <medved@iki.fi> ++ * ++ * $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 <linux/config.h> ++#include <linux/icmpv6.h> ++#include <linux/net.h> ++#include <linux/spinlock.h> ++#include <linux/timer.h> ++#include <linux/netdevice.h> ++#include <net/ipv6.h> ++#include <net/addrconf.h> ++#include <net/ip6_route.h> ++#include <net/mipv6.h> ++ ++#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); ++} +--- /dev/null ++++ linux-2.4.27/net/ipv6/mobile_ip6/rr_crypto.c +@@ -0,0 +1,255 @@ ++/* ++ * rr_cookie.c - Mobile IPv6 return routability crypto ++ * Author : Henrik Petander <henrik.petander@hut.fi> ++ * ++ * $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 <linux/kernel.h> ++#include <linux/types.h> ++#include <linux/spinlock.h> ++#include <linux/sched.h> ++#include <linux/timer.h> ++#include <linux/in6.h> ++#include <linux/init.h> ++#include <linux/random.h> ++ ++#include <net/ipv6.h> ++ ++#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 +--- /dev/null ++++ linux-2.4.27/net/ipv6/mobile_ip6/rr_crypto.h +@@ -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 <linux/in6.h> ++ ++/* 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 */ +--- /dev/null ++++ linux-2.4.27/net/ipv6/mobile_ip6/sortedlist.c +@@ -0,0 +1,349 @@ ++/** ++ * Sorted list - linked list with sortkey. ++ * ++ * Authors: ++ * Jaakko Laine <medved@iki.fi> ++ * ++ * $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 <linux/kernel.h> ++#include <linux/types.h> ++#include <linux/list.h> ++#include <linux/slab.h> ++#include <linux/spinlock.h> ++#include <linux/string.h> ++ ++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; ++} +--- /dev/null ++++ linux-2.4.27/net/ipv6/mobile_ip6/sortedlist.h +@@ -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)); +--- /dev/null ++++ linux-2.4.27/net/ipv6/mobile_ip6/stats.c +@@ -0,0 +1,90 @@ ++/* ++ * Statistics module ++ * ++ * Authors: ++ * Sami Kivisaari <skivisaa@cc.hut.fi> ++ * ++ * $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 <linux/config.h> ++#include <linux/proc_fs.h> ++#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<sizeof(int_stats) / sizeof(struct inf); i++) { ++ len += sprintf(buffer + len, "%s = %d\n", ++ int_stats[i].name, *int_stats[i].value); ++ } ++ ++ *start = buffer + offset; ++ ++ len -= offset; ++ ++ if(len > 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"); ++} +--- /dev/null ++++ linux-2.4.27/net/ipv6/mobile_ip6/stats.h +@@ -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 +--- /dev/null ++++ linux-2.4.27/net/ipv6/mobile_ip6/tunnel.h +@@ -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 <linux/in6.h> ++#include <linux/if_arp.h> ++#include <net/ipv6_tunnel.h> ++ ++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 ++ +--- /dev/null ++++ linux-2.4.27/net/ipv6/mobile_ip6/tunnel_ha.c +@@ -0,0 +1,264 @@ ++/* ++ * IPv6-IPv6 tunneling module ++ * ++ * Authors: ++ * Sami Kivisaari <skivisaa@cc.hut.fi> ++ * Ville Nuorvala <vnuorval@tml.hut.fi> ++ * ++ * $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 <linux/net.h> ++#include <linux/skbuff.h> ++#include <linux/ipv6.h> ++#include <linux/net.h> ++#include <linux/netdevice.h> ++#include <linux/init.h> ++#include <linux/route.h> ++#include <linux/ipv6_route.h> ++ ++#ifdef CONFIG_SYSCTL ++#include <linux/sysctl.h> ++#endif /* CONFIG_SYSCTL */ ++ ++#include <net/protocol.h> ++#include <net/ipv6.h> ++#include <net/ip6_route.h> ++#include <net/dst.h> ++#include <net/addrconf.h> ++ ++#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); ++} ++ +--- /dev/null ++++ linux-2.4.27/net/ipv6/mobile_ip6/tunnel_ha.h +@@ -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 +--- /dev/null ++++ linux-2.4.27/net/ipv6/mobile_ip6/tunnel_mn.c +@@ -0,0 +1,160 @@ ++/* ++ * IPv6-IPv6 tunneling module ++ * ++ * Authors: ++ * Sami Kivisaari <skivisaa@cc.hut.fi> ++ * Ville Nuorvala <vnuorval@tml.hut.fi> ++ * ++ * $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 <linux/net.h> ++#include <linux/skbuff.h> ++#include <linux/ipv6.h> ++#include <linux/net.h> ++#include <linux/netdevice.h> ++#include <linux/init.h> ++#include <linux/route.h> ++#include <linux/ipv6_route.h> ++ ++#ifdef CONFIG_SYSCTL ++#include <linux/sysctl.h> ++#endif /* CONFIG_SYSCTL */ ++ ++#include <net/protocol.h> ++#include <net/ipv6.h> ++#include <net/ip6_route.h> ++#include <net/dst.h> ++#include <net/addrconf.h> ++ ++#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(); ++} +--- /dev/null ++++ linux-2.4.27/net/ipv6/mobile_ip6/tunnel_mn.h +@@ -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 +--- /dev/null ++++ linux-2.4.27/net/ipv6/mobile_ip6/util.h +@@ -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 <linux/in6.h> ++#include <asm/byteorder.h> ++ ++/** ++ * 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 */ +--- linux-2.4.27/net/ipv6/ndisc.c~mipv6-1.1-v2.4.26 ++++ linux-2.4.27/net/ipv6/ndisc.c +@@ -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 <net/ip6_route.h> + #include <net/addrconf.h> + #include <net/icmp.h> ++#include <net/mipglue.h> + + #include <net/checksum.h> + #include <linux/proc_fs.h> +@@ -187,6 +189,8 @@ + 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 @@ + */ + + 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 @@ + 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 @@ + 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 @@ + 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 @@ + 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 @@ + 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 @@ + 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 @@ + "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 @@ + 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 @@ + 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) +--- linux-2.4.27/net/ipv6/raw.c~mipv6-1.1-v2.4.26 ++++ linux-2.4.27/net/ipv6/raw.c +@@ -43,6 +43,7 @@ + #include <net/transp_v6.h> + #include <net/udp.h> + #include <net/inet_common.h> ++#include <net/mipglue.h> + + #include <net/rawv6.h> + +@@ -641,6 +642,7 @@ + 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); +--- linux-2.4.27/net/ipv6/route.c~mipv6-1.1-v2.4.26 ++++ linux-2.4.27/net/ipv6/route.c +@@ -49,6 +49,7 @@ + #include <net/addrconf.h> + #include <net/tcp.h> + #include <linux/rtnetlink.h> ++#include <net/mipglue.h> + + #include <asm/uaccess.h> + +@@ -363,12 +364,8 @@ + 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 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 @@ + 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 @@ + 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 @@ + 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 @@ + 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 @@ + 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 @@ + 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; +--- linux-2.4.27/net/ipv6/tcp_ipv6.c~mipv6-1.1-v2.4.26 ++++ linux-2.4.27/net/ipv6/tcp_ipv6.c +@@ -50,6 +50,7 @@ + #include <net/addrconf.h> + #include <net/ip6_route.h> + #include <net/inet_ecn.h> ++#include <net/mipglue.h> + + #include <asm/uaccess.h> + +@@ -557,6 +558,7 @@ + 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 @@ + + 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 @@ + 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 @@ + #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 @@ + 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 @@ + 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 @@ + return -sk->err_soft; + } + +- ip6_dst_store(sk, dst, NULL); ++ ip6_dst_store(sk, dst, NULL, NULL); + } + + skb->dst = dst_clone(dst); +--- linux-2.4.27/net/ipv6/udp.c~mipv6-1.1-v2.4.26 ++++ linux-2.4.27/net/ipv6/udp.c +@@ -48,6 +48,7 @@ + #include <net/ip.h> + #include <net/udp.h> + #include <net/inet_common.h> ++#include <net/mipglue.h> + + #include <net/checksum.h> + +@@ -232,6 +233,7 @@ + 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 @@ + + 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 @@ + 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); +@@ -889,6 +906,7 @@ + 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; +--- linux-2.4.27/net/netsyms.c~mipv6-1.1-v2.4.26 ++++ linux-2.4.27/net/netsyms.c +@@ -190,6 +190,7 @@ + #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); |