diff options
author | Denys Dmytriyenko <denis@denix.org> | 2009-03-17 14:32:59 -0400 |
---|---|---|
committer | Denys Dmytriyenko <denis@denix.org> | 2009-03-17 14:32:59 -0400 |
commit | 709c4d66e0b107ca606941b988bad717c0b45d9b (patch) | |
tree | 37ee08b1eb308f3b2b6426d5793545c38396b838 /packages/linux/files/mipv6-1.1-v2.4.25.patch | |
parent | fa6cd5a3b993f16c27de4ff82b42684516d433ba (diff) |
rename packages/ to recipes/ per earlier agreement
See links below for more details:
http://thread.gmane.org/gmane.comp.handhelds.openembedded/21326
http://thread.gmane.org/gmane.comp.handhelds.openembedded/21816
Signed-off-by: Denys Dmytriyenko <denis@denix.org>
Acked-by: Mike Westerhof <mwester@dls.net>
Acked-by: Philip Balister <philip@balister.org>
Acked-by: Khem Raj <raj.khem@gmail.com>
Acked-by: Marcin Juszkiewicz <hrw@openembedded.org>
Acked-by: Koen Kooi <koen@openembedded.org>
Acked-by: Frans Meulenbroeks <fransmeulenbroeks@gmail.com>
Diffstat (limited to 'packages/linux/files/mipv6-1.1-v2.4.25.patch')
-rw-r--r-- | packages/linux/files/mipv6-1.1-v2.4.25.patch | 19832 |
1 files changed, 0 insertions, 19832 deletions
diff --git a/packages/linux/files/mipv6-1.1-v2.4.25.patch b/packages/linux/files/mipv6-1.1-v2.4.25.patch deleted file mode 100644 index b7f9b8fc8a..0000000000 --- a/packages/linux/files/mipv6-1.1-v2.4.25.patch +++ /dev/null @@ -1,19832 +0,0 @@ -diff -uprN linux-2.4.25.old/Documentation/Configure.help linux-2.4.25/Documentation/Configure.help ---- linux-2.4.25.old/Documentation/Configure.help 2004-06-26 11:22:00.000000000 +0100 -+++ linux-2.4.25/Documentation/Configure.help 2004-06-26 11:29:29.000000000 +0100 -@@ -6204,6 +6204,57 @@ CONFIG_IPV6 - - It is safe to say N here for now. - -+IPv6: IPv6 over IPv6 Tunneling (EXPERIMENTAL) -+CONFIG_IPV6_TUNNEL -+ Experimental IP6-IP6 tunneling. You must select this, if you want -+ to use CONFIG_IPV6_MOBILITY. More information in MIPL Mobile IPv6 -+ instructions. -+ -+ If you don't want IP6-IP6 tunnels and Mobile IPv6, say N. -+ -+IPv6: Mobility Support (EXPERIMENTAL) -+CONFIG_IPV6_MOBILITY -+ This is experimental support for the upcoming specification of -+ Mobile IPv6. Mobile IPv6 allows nodes to seamlessly move between -+ networks without changing their IP addresses, thus allowing them to -+ maintain upper layer connections (e.g. TCP). Selecting this option -+ allows your computer to act as a Correspondent Node (CN). A MIPv6 -+ Mobile Node will be able to communicate with the CN and use route -+ optimization. -+ -+ For more information and configuration details, see -+ http://www.mipl.mediapoli.com/. -+ -+ If unsure, say N. -+ -+MIPv6: Mobile Node Support -+CONFIG_IPV6_MOBILITY_MN -+ If you want your computer to be a MIPv6 Mobile Node (MN), select -+ this option. You must configure MN using the userspace tools -+ available at http://www.mipl.mediapoli.com/download/mipv6-tools/. -+ -+ If your computer is stationary, or you are unsure if you need this, -+ say N. Note that you will need a properly configured MIPv6 Home -+ Agent to use any Mobile Nodes. -+ -+MIPv6: Home Agent Support -+CONFIG_IPV6_MOBILITY_HA -+ If you want your router to serve as a MIPv6 Home Agent (HA), select -+ this option. You must configure HA using the userspace tools -+ available at http://www.mipl.mediapoli.com/download/mipv6-tools/. -+ -+ If your computer is not a router, or you are unsure if you need -+ this, say N. -+ -+MIPv6: Debug messages -+CONFIG_IPV6_MOBILITY_DEBUG -+ MIPL Mobile IPv6 can produce a lot of debugging messages. There are -+ eight debug levels (0 through 7) and the level is controlled via -+ /proc/sys/net/ipv6/mobility/debuglevel. Since MIPL is still -+ experimental, you might want to say Y here. -+ -+ Be sure to say Y and record debug messages when submitting a bug -+ report. - The SCTP Protocol (EXPERIMENTAL) - CONFIG_IP_SCTP - Stream Control Transmission Protocol -diff -uprN linux-2.4.25.old/Documentation/DocBook/Makefile linux-2.4.25/Documentation/DocBook/Makefile ---- linux-2.4.25.old/Documentation/DocBook/Makefile 2002-11-28 23:53:08.000000000 +0000 -+++ linux-2.4.25/Documentation/DocBook/Makefile 2004-06-26 11:29:29.000000000 +0100 -@@ -2,7 +2,7 @@ BOOKS := wanbook.sgml z8530book.sgml mca - kernel-api.sgml parportbook.sgml kernel-hacking.sgml \ - kernel-locking.sgml via-audio.sgml mousedrivers.sgml sis900.sgml \ - deviceiobook.sgml procfs-guide.sgml tulip-user.sgml \ -- journal-api.sgml -+ journal-api.sgml mip6-func.sgml - - PS := $(patsubst %.sgml, %.ps, $(BOOKS)) - PDF := $(patsubst %.sgml, %.pdf, $(BOOKS)) -@@ -86,6 +86,9 @@ videobook.sgml: videobook.tmpl $(TOPDIR) - procfs-guide.sgml: procfs-guide.tmpl procfs_example.sgml - $(TOPDIR)/scripts/docgen < procfs-guide.tmpl >$@ - -+mip6-func.sgml: mip6-func.tmpl -+ $(TOPDIR)/scripts/docgen <$< >$@ -+ - APISOURCES := $(TOPDIR)/drivers/media/video/videodev.c \ - $(TOPDIR)/arch/i386/kernel/irq.c \ - $(TOPDIR)/arch/i386/kernel/mca.c \ -diff -uprN linux-2.4.25.old/Documentation/DocBook/mip6-func.tmpl linux-2.4.25/Documentation/DocBook/mip6-func.tmpl ---- linux-2.4.25.old/Documentation/DocBook/mip6-func.tmpl 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/Documentation/DocBook/mip6-func.tmpl 2004-06-26 11:29:29.000000000 +0100 -@@ -0,0 +1,756 @@ -+<!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> -diff -uprN linux-2.4.25.old/include/linux/icmpv6.h linux-2.4.25/include/linux/icmpv6.h ---- linux-2.4.25.old/include/linux/icmpv6.h 2003-08-25 12:44:44.000000000 +0100 -+++ linux-2.4.25/include/linux/icmpv6.h 2004-06-26 11:29:29.000000000 +0100 -@@ -40,14 +40,16 @@ struct icmp6hdr { - struct icmpv6_nd_ra { - __u8 hop_limit; - #if defined(__LITTLE_ENDIAN_BITFIELD) -- __u8 reserved:6, -+ __u8 reserved:5, -+ home_agent:1, - other:1, - managed:1; - - #elif defined(__BIG_ENDIAN_BITFIELD) - __u8 managed:1, - other:1, -- reserved:6; -+ home_agent:1, -+ reserved:5; - #else - #error "Please fix <asm/byteorder.h>" - #endif -@@ -70,6 +72,7 @@ struct icmp6hdr { - #define icmp6_addrconf_managed icmp6_dataun.u_nd_ra.managed - #define icmp6_addrconf_other icmp6_dataun.u_nd_ra.other - #define icmp6_rt_lifetime icmp6_dataun.u_nd_ra.rt_lifetime -+#define icmp6_home_agent icmp6_dataun.u_nd_ra.home_agent - }; - - -diff -uprN linux-2.4.25.old/include/linux/if_arp.h linux-2.4.25/include/linux/if_arp.h ---- linux-2.4.25.old/include/linux/if_arp.h 2002-02-25 19:38:13.000000000 +0000 -+++ linux-2.4.25/include/linux/if_arp.h 2004-06-26 11:29:29.000000000 +0100 -@@ -59,7 +59,7 @@ - #define ARPHRD_RAWHDLC 518 /* Raw HDLC */ - - #define ARPHRD_TUNNEL 768 /* IPIP tunnel */ --#define ARPHRD_TUNNEL6 769 /* IPIP6 tunnel */ -+#define ARPHRD_TUNNEL6 769 /* IP6IP6 tunnel */ - #define ARPHRD_FRAD 770 /* Frame Relay Access Device */ - #define ARPHRD_SKIP 771 /* SKIP vif */ - #define ARPHRD_LOOPBACK 772 /* Loopback device */ -diff -uprN linux-2.4.25.old/include/linux/in6.h linux-2.4.25/include/linux/in6.h ---- linux-2.4.25.old/include/linux/in6.h 2003-06-13 15:51:38.000000000 +0100 -+++ linux-2.4.25/include/linux/in6.h 2004-06-26 11:29:29.000000000 +0100 -@@ -142,6 +142,11 @@ struct in6_flowlabel_req - #define IPV6_TLV_JUMBO 194 - - /* -+ * Mobile IPv6 TLV options. -+ */ -+#define MIPV6_TLV_HOMEADDR 201 -+ -+/* - * IPV6 socket options - */ - -diff -uprN linux-2.4.25.old/include/linux/ipv6.h linux-2.4.25/include/linux/ipv6.h ---- linux-2.4.25.old/include/linux/ipv6.h 2003-11-28 18:26:21.000000000 +0000 -+++ linux-2.4.25/include/linux/ipv6.h 2004-06-26 11:29:29.000000000 +0100 -@@ -29,6 +29,7 @@ struct in6_ifreq { - - #define IPV6_SRCRT_STRICT 0x01 /* this hop must be a neighbor */ - #define IPV6_SRCRT_TYPE_0 0 /* IPv6 type 0 Routing Header */ -+#define IPV6_SRCRT_TYPE_2 2 /* type 2 for Mobile IPv6 */ - - /* - * routing header -@@ -71,6 +72,19 @@ struct rt0_hdr { - struct in6_addr addr[0]; - - #define rt0_type rt_hdr.type -+ -+}; -+ -+/* -+ * routing header type 2 -+ */ -+ -+struct rt2_hdr { -+ struct ipv6_rt_hdr rt_hdr; -+ __u32 reserved; -+ struct in6_addr addr; -+ -+#define rt2_type rt_hdr.type; - }; - - /* -@@ -156,12 +170,16 @@ enum { - struct inet6_skb_parm - { - int iif; -+ __u8 mipv6_flags; - __u16 ra; - __u16 hop; - __u16 auth; - __u16 dst0; - __u16 srcrt; -+ __u16 srcrt2; -+ __u16 hao; - __u16 dst1; -+ struct in6_addr hoa; - }; - - #endif -diff -uprN linux-2.4.25.old/include/linux/ipv6_route.h linux-2.4.25/include/linux/ipv6_route.h ---- linux-2.4.25.old/include/linux/ipv6_route.h 2003-11-28 18:26:21.000000000 +0000 -+++ linux-2.4.25/include/linux/ipv6_route.h 2004-06-26 11:29:29.000000000 +0100 -@@ -33,6 +33,7 @@ enum - #define RTF_CACHE 0x01000000 /* cache entry */ - #define RTF_FLOW 0x02000000 /* flow significant route */ - #define RTF_POLICY 0x04000000 /* policy route */ -+#define RTF_MOBILENODE 0x10000000 /* for routing to Mobile Node */ - - #define RTF_LOCAL 0x80000000 - -diff -uprN linux-2.4.25.old/include/linux/ipv6_tunnel.h linux-2.4.25/include/linux/ipv6_tunnel.h ---- linux-2.4.25.old/include/linux/ipv6_tunnel.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/include/linux/ipv6_tunnel.h 2004-06-26 11:29:29.000000000 +0100 -@@ -0,0 +1,34 @@ -+/* -+ * $Id$ -+ */ -+ -+#ifndef _IPV6_TUNNEL_H -+#define _IPV6_TUNNEL_H -+ -+#define IPV6_TLV_TNL_ENCAP_LIMIT 4 -+#define IPV6_DEFAULT_TNL_ENCAP_LIMIT 4 -+ -+/* don't add encapsulation limit if one isn't present in inner packet */ -+#define IP6_TNL_F_IGN_ENCAP_LIMIT 0x1 -+/* copy the traffic class field from the inner packet */ -+#define IP6_TNL_F_USE_ORIG_TCLASS 0x2 -+/* copy the flowlabel from the inner packet */ -+#define IP6_TNL_F_USE_ORIG_FLOWLABEL 0x4 -+/* created and maintained from within the kernel */ -+#define IP6_TNL_F_KERNEL_DEV 0x8 -+/* being used for Mobile IPv6 */ -+#define IP6_TNL_F_MIP6_DEV 0x10 -+ -+struct ip6_tnl_parm { -+ char name[IFNAMSIZ]; /* name of tunnel device */ -+ int link; /* ifindex of underlying L2 interface */ -+ __u8 proto; /* tunnel protocol */ -+ __u8 encap_limit; /* encapsulation limit for tunnel */ -+ __u8 hop_limit; /* hop limit for tunnel */ -+ __u32 flowinfo; /* traffic class and flowlabel for tunnel */ -+ __u32 flags; /* tunnel flags */ -+ struct in6_addr laddr; /* local tunnel end-point address */ -+ struct in6_addr raddr; /* remote tunnel end-point address */ -+}; -+ -+#endif -diff -uprN linux-2.4.25.old/include/linux/rtnetlink.h linux-2.4.25/include/linux/rtnetlink.h ---- linux-2.4.25.old/include/linux/rtnetlink.h 2004-02-18 13:36:32.000000000 +0000 -+++ linux-2.4.25/include/linux/rtnetlink.h 2004-06-26 11:29:29.000000000 +0100 -@@ -309,15 +309,17 @@ enum - IFA_LABEL, - IFA_BROADCAST, - IFA_ANYCAST, -- IFA_CACHEINFO -+ IFA_CACHEINFO, -+ IFA_HOMEAGENT - }; - --#define IFA_MAX IFA_CACHEINFO -+#define IFA_MAX IFA_HOMEAGENT - - /* ifa_flags */ - - #define IFA_F_SECONDARY 0x01 - -+#define IFA_F_HOMEADDR 0x10 - #define IFA_F_DEPRECATED 0x20 - #define IFA_F_TENTATIVE 0x40 - #define IFA_F_PERMANENT 0x80 -diff -uprN linux-2.4.25.old/include/linux/skbuff.h linux-2.4.25/include/linux/skbuff.h ---- linux-2.4.25.old/include/linux/skbuff.h 2003-08-25 12:44:44.000000000 +0100 -+++ linux-2.4.25/include/linux/skbuff.h 2004-06-26 11:29:29.000000000 +0100 -@@ -177,7 +177,7 @@ struct sk_buff { - * want to keep them across layers you have to do a skb_clone() - * first. This is owned by whoever has the skb queued ATM. - */ -- char cb[48]; -+ char cb[64]; - - unsigned int len; /* Length of actual data */ - unsigned int data_len; -diff -uprN linux-2.4.25.old/include/linux/sysctl.h linux-2.4.25/include/linux/sysctl.h ---- linux-2.4.25.old/include/linux/sysctl.h 2004-02-18 13:36:32.000000000 +0000 -+++ linux-2.4.25/include/linux/sysctl.h 2004-06-26 11:29:29.000000000 +0100 -@@ -387,7 +387,24 @@ enum { - NET_IPV6_NEIGH=17, - NET_IPV6_ROUTE=18, - NET_IPV6_ICMP=19, -- NET_IPV6_BINDV6ONLY=20 -+ NET_IPV6_BINDV6ONLY=20, -+ NET_IPV6_MOBILITY=26 -+}; -+ -+/* /proc/sys/net/ipv6/mobility */ -+enum { -+ NET_IPV6_MOBILITY_DEBUG=1, -+ NET_IPV6_MOBILITY_TUNNEL_SITELOCAL=2, -+ NET_IPV6_MOBILITY_ROUTER_SOLICITATION_MAX_SENDTIME=3, -+ NET_IPV6_MOBILITY_ROUTER_REACH=4, -+ NET_IPV6_MOBILITY_MDETECT_MECHANISM=5, -+ NET_IPV6_MOBILITY_RETROUT=6, -+ NET_IPV6_MOBILITY_MAX_TNLS=7, -+ NET_IPV6_MOBILITY_MIN_TNLS=8, -+ NET_IPV6_MOBILITY_BINDING_REFRESH=9, -+ NET_IPV6_MOBILITY_BU_F_LLADDR=10, -+ NET_IPV6_MOBILITY_BU_F_KEYMGM=11, -+ NET_IPV6_MOBILITY_BU_F_CN_ACK=12 - }; - - enum { -diff -uprN linux-2.4.25.old/include/net/addrconf.h linux-2.4.25/include/net/addrconf.h ---- linux-2.4.25.old/include/net/addrconf.h 2003-08-25 12:44:44.000000000 +0100 -+++ linux-2.4.25/include/net/addrconf.h 2004-06-26 11:29:29.000000000 +0100 -@@ -16,9 +16,11 @@ struct prefix_info { - #if defined(__BIG_ENDIAN_BITFIELD) - __u8 onlink : 1, - autoconf : 1, -- reserved : 6; -+ router_address : 1, -+ reserved : 5; - #elif defined(__LITTLE_ENDIAN_BITFIELD) -- __u8 reserved : 6, -+ __u8 reserved : 5, -+ router_address : 1, - autoconf : 1, - onlink : 1; - #else -@@ -55,6 +57,7 @@ extern int ipv6_chk_addr(struct in6_ad - struct net_device *dev); - extern struct inet6_ifaddr * ipv6_get_ifaddr(struct in6_addr *addr, - struct net_device *dev); -+extern void ipv6_del_addr(struct inet6_ifaddr *ifp); - extern int ipv6_get_saddr(struct dst_entry *dst, - struct in6_addr *daddr, - struct in6_addr *saddr); -@@ -85,7 +88,9 @@ extern void ipv6_mc_up(struct inet6_dev - extern void ipv6_mc_down(struct inet6_dev *idev); - extern void ipv6_mc_init_dev(struct inet6_dev *idev); - extern void ipv6_mc_destroy_dev(struct inet6_dev *idev); -+extern void addrconf_dad_start(struct inet6_ifaddr *ifp, int flags); - extern void addrconf_dad_failure(struct inet6_ifaddr *ifp); -+extern void addrconf_dad_completed(struct inet6_ifaddr *ifp); - - extern int ipv6_chk_mcast_addr(struct net_device *dev, struct in6_addr *group, - struct in6_addr *src_addr); -@@ -116,6 +121,9 @@ extern int ipv6_chk_acast_addr(struct - extern int register_inet6addr_notifier(struct notifier_block *nb); - extern int unregister_inet6addr_notifier(struct notifier_block *nb); - -+extern int ipv6_generate_eui64(u8 *eui, struct net_device *dev); -+extern int ipv6_inherit_eui64(u8 *eui, struct inet6_dev *idev); -+ - static inline struct inet6_dev * - __in6_dev_get(struct net_device *dev) - { -diff -uprN linux-2.4.25.old/include/net/ip6_route.h linux-2.4.25/include/net/ip6_route.h ---- linux-2.4.25.old/include/net/ip6_route.h 2003-06-13 15:51:39.000000000 +0100 -+++ linux-2.4.25/include/net/ip6_route.h 2004-06-26 11:29:29.000000000 +0100 -@@ -2,6 +2,7 @@ - #define _NET_IP6_ROUTE_H - - #define IP6_RT_PRIO_FW 16 -+#define IP6_RT_PRIO_MIPV6 64 - #define IP6_RT_PRIO_USER 1024 - #define IP6_RT_PRIO_ADDRCONF 256 - #define IP6_RT_PRIO_KERN 512 -@@ -40,6 +41,9 @@ extern int ipv6_route_ioctl(unsigned i - - extern int ip6_route_add(struct in6_rtmsg *rtmsg, - struct nlmsghdr *); -+ -+extern int ip6_route_del(struct in6_rtmsg *rtmsg, -+ struct nlmsghdr *); - extern int ip6_del_rt(struct rt6_info *, - struct nlmsghdr *); - -@@ -99,7 +103,8 @@ extern rwlock_t rt6_lock; - */ - - static inline void ip6_dst_store(struct sock *sk, struct dst_entry *dst, -- struct in6_addr *daddr) -+ struct in6_addr *daddr, -+ struct in6_addr *saddr) - { - struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6; - struct rt6_info *rt = (struct rt6_info *) dst; -@@ -107,6 +112,9 @@ static inline void ip6_dst_store(struct - write_lock(&sk->dst_lock); - __sk_dst_set(sk, dst); - np->daddr_cache = daddr; -+#ifdef CONFIG_IPV6_SUBTREES -+ np->saddr_cache = saddr; -+#endif - np->dst_cookie = rt->rt6i_node ? rt->rt6i_node->fn_sernum : 0; - write_unlock(&sk->dst_lock); - } -diff -uprN linux-2.4.25.old/include/net/ipv6.h linux-2.4.25/include/net/ipv6.h ---- linux-2.4.25.old/include/net/ipv6.h 2003-11-28 18:26:21.000000000 +0000 -+++ linux-2.4.25/include/net/ipv6.h 2004-06-26 11:29:29.000000000 +0100 -@@ -37,6 +37,7 @@ - #define NEXTHDR_ICMP 58 /* ICMP for IPv6. */ - #define NEXTHDR_NONE 59 /* No next header */ - #define NEXTHDR_DEST 60 /* Destination options header. */ -+#define NEXTHDR_MH 135 /* Mobility header, RFC 3775 */ - - #define NEXTHDR_MAX 255 - -@@ -145,9 +146,12 @@ struct ipv6_txoptions - __u16 opt_flen; /* after fragment hdr */ - __u16 opt_nflen; /* before fragment hdr */ - -+ __u8 mipv6_flags; /* flags set by MIPv6 */ -+ - struct ipv6_opt_hdr *hopopt; - struct ipv6_opt_hdr *dst0opt; -- struct ipv6_rt_hdr *srcrt; /* Routing Header */ -+ struct ipv6_rt_hdr *srcrt; /* Routing Header Type 0 */ -+ struct ipv6_rt_hdr *srcrt2; /* Routing Header Type 2 */ - struct ipv6_opt_hdr *auth; - struct ipv6_opt_hdr *dst1opt; - -@@ -256,6 +260,38 @@ static inline int ipv6_addr_any(const st - a->s6_addr32[2] | a->s6_addr32[3] ) == 0); - } - -+static inline void ipv6_addr_prefix(struct in6_addr *pfx, -+ const struct in6_addr *addr, int plen) -+{ -+ /* caller must guarantee 0 <= plen <= 128 */ -+ int o = plen >> 3, -+ b = plen & 0x7; -+ -+ memcpy(pfx->s6_addr, addr, o); -+ if (b != 0) { -+ pfx->s6_addr[o] = addr->s6_addr[o] & (0xff00 >> b); -+ o++; -+ } -+ if (o < 16) -+ memset(pfx->s6_addr + o, 0, 16 - o); -+} -+ -+static inline int ipv6_prefix_cmp(const struct in6_addr *p1, -+ const struct in6_addr *p2, int plen) -+{ -+ int b = plen&0x7; -+ int o = plen>>3; -+ int res = 0; -+ -+ if (o > 0) -+ res = memcmp(&p1->s6_addr[0], &p2->s6_addr[0], o); -+ if (res == 0 && b > 0) { -+ __u8 m = (0xff00 >> b) & 0xff; -+ res = (p1->s6_addr[o] & m) - (p2->s6_addr[o] & m); -+ } -+ return res; -+} -+ - /* - * Prototypes exported by ipv6 - */ -diff -uprN linux-2.4.25.old/include/net/ipv6_tunnel.h linux-2.4.25/include/net/ipv6_tunnel.h ---- linux-2.4.25.old/include/net/ipv6_tunnel.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/include/net/ipv6_tunnel.h 2004-06-26 11:29:29.000000000 +0100 -@@ -0,0 +1,92 @@ -+/* -+ * $Id$ -+ */ -+ -+#ifndef _NET_IPV6_TUNNEL_H -+#define _NET_IPV6_TUNNEL_H -+ -+#include <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 -diff -uprN linux-2.4.25.old/include/net/mipglue.h linux-2.4.25/include/net/mipglue.h ---- linux-2.4.25.old/include/net/mipglue.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/include/net/mipglue.h 2004-06-26 11:29:29.000000000 +0100 -@@ -0,0 +1,266 @@ -+/* -+ * Glue for Mobility support integration to IPv6 -+ * -+ * Authors: -+ * Antti Tuominen <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 */ -diff -uprN linux-2.4.25.old/include/net/mipv6.h linux-2.4.25/include/net/mipv6.h ---- linux-2.4.25.old/include/net/mipv6.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/include/net/mipv6.h 2004-06-26 11:29:29.000000000 +0100 -@@ -0,0 +1,258 @@ -+/* -+ * Mobile IPv6 header-file -+ * -+ * Authors: -+ * Sami Kivisaari <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 */ -diff -uprN linux-2.4.25.old/include/net/ndisc.h linux-2.4.25/include/net/ndisc.h ---- linux-2.4.25.old/include/net/ndisc.h 2002-11-28 23:53:15.000000000 +0000 -+++ linux-2.4.25/include/net/ndisc.h 2004-06-26 11:29:29.000000000 +0100 -@@ -21,6 +21,10 @@ - #define ND_OPT_REDIRECT_HDR 4 - #define ND_OPT_MTU 5 - -+/* Mobile IPv6 specific ndisc options */ -+#define ND_OPT_RTR_ADV_INTERVAL 7 -+#define ND_OPT_HOME_AGENT_INFO 8 -+ - #define MAX_RTR_SOLICITATION_DELAY HZ - - #define ND_REACHABLE_TIME (30*HZ) -@@ -57,7 +61,7 @@ struct nd_opt_hdr { - } __attribute__((__packed__)); - - struct ndisc_options { -- struct nd_opt_hdr *nd_opt_array[7]; -+ struct nd_opt_hdr *nd_opt_array[10]; - struct nd_opt_hdr *nd_opt_piend; - }; - -@@ -67,6 +71,8 @@ struct ndisc_options { - #define nd_opts_pi_end nd_opt_piend - #define nd_opts_rh nd_opt_array[ND_OPT_REDIRECT_HDR] - #define nd_opts_mtu nd_opt_array[ND_OPT_MTU] -+#define nd_opts_rai nd_opt_array[ND_OPT_RTR_ADV_INTERVAL] -+#define nd_opts_hai nd_opt_array[ND_OPT_HOME_AGENT_INFO] - - extern struct nd_opt_hdr *ndisc_next_option(struct nd_opt_hdr *cur, struct nd_opt_hdr *end); - extern struct ndisc_options *ndisc_parse_options(u8 *opt, int opt_len, struct ndisc_options *ndopts); -@@ -83,6 +89,15 @@ extern void ndisc_send_ns(struct net_d - struct in6_addr *daddr, - struct in6_addr *saddr); - -+extern void ndisc_send_na(struct net_device *dev, -+ struct neighbour *neigh, -+ struct in6_addr *daddr, -+ struct in6_addr *solicited_addr, -+ int router, -+ int solicited, -+ int override, -+ int inc_opt); -+ - extern void ndisc_send_rs(struct net_device *dev, - struct in6_addr *saddr, - struct in6_addr *daddr); -diff -uprN linux-2.4.25.old/include/net/sock.h linux-2.4.25/include/net/sock.h ---- linux-2.4.25.old/include/net/sock.h 2004-02-18 13:36:32.000000000 +0000 -+++ linux-2.4.25/include/net/sock.h 2004-06-26 11:29:30.000000000 +0100 -@@ -149,7 +149,9 @@ struct ipv6_pinfo { - struct in6_addr rcv_saddr; - struct in6_addr daddr; - struct in6_addr *daddr_cache; -- -+#if defined(CONFIG_IPV6_SUBTREES) -+ struct in6_addr *saddr_cache; -+#endif - __u32 flow_label; - __u32 frag_size; - int hop_limit; -diff -uprN linux-2.4.25.old/net/Makefile linux-2.4.25/net/Makefile ---- linux-2.4.25.old/net/Makefile 2004-06-26 11:22:00.000000000 +0100 -+++ linux-2.4.25/net/Makefile 2004-06-26 11:29:30.000000000 +0100 -@@ -7,7 +7,7 @@ - - O_TARGET := network.o - --mod-subdirs := ipv4/netfilter ipv6/netfilter ipx irda bluetooth atm netlink sched core sctp -+mod-subdirs := ipv4/netfilter ipv6/netfilter ipx irda bluetooth atm netlink sched core sctp ipv6 - export-objs := netsyms.o - - subdir-y := core ethernet -@@ -25,6 +25,7 @@ subdir-$(CONFIG_IP_SCTP) += sctp - ifneq ($(CONFIG_IPV6),n) - ifneq ($(CONFIG_IPV6),) - subdir-$(CONFIG_NETFILTER) += ipv6/netfilter -+subdir-$(CONFIG_IPV6_MOBILITY) += ipv6/mobile_ip6 - endif - endif - -diff -uprN linux-2.4.25.old/net/core/neighbour.c linux-2.4.25/net/core/neighbour.c ---- linux-2.4.25.old/net/core/neighbour.c 2004-02-18 13:36:32.000000000 +0000 -+++ linux-2.4.25/net/core/neighbour.c 2004-06-26 11:29:30.000000000 +0100 -@@ -386,7 +386,7 @@ struct pneigh_entry * pneigh_lookup(stru - if (!creat) - return NULL; - -- n = kmalloc(sizeof(*n) + key_len, GFP_KERNEL); -+ n = kmalloc(sizeof(*n) + key_len, GFP_ATOMIC); - if (n == NULL) - return NULL; - -diff -uprN linux-2.4.25.old/net/ipv6/Config.in linux-2.4.25/net/ipv6/Config.in ---- linux-2.4.25.old/net/ipv6/Config.in 2001-12-21 17:42:05.000000000 +0000 -+++ linux-2.4.25/net/ipv6/Config.in 2004-06-26 11:29:30.000000000 +0100 -@@ -1,10 +1,16 @@ - # - # IPv6 configuration - # -- -+bool ' IPv6: routing by source address (EXPERIMENTAL)' CONFIG_IPV6_SUBTREES - #bool ' IPv6: flow policy support' CONFIG_RT6_POLICY - #bool ' IPv6: firewall support' CONFIG_IPV6_FIREWALL - -+if [ "$CONFIG_IPV6" != "n" ]; then -+ dep_tristate ' IPv6: IPv6 over IPv6 Tunneling (EXPERIMENTAL)' CONFIG_IPV6_TUNNEL $CONFIG_IPV6 -+fi -+ -+source net/ipv6/mobile_ip6/Config.in -+ - if [ "$CONFIG_NETFILTER" != "n" ]; then - source net/ipv6/netfilter/Config.in - fi -diff -uprN linux-2.4.25.old/net/ipv6/Makefile linux-2.4.25/net/ipv6/Makefile ---- linux-2.4.25.old/net/ipv6/Makefile 2003-11-28 18:26:21.000000000 +0000 -+++ linux-2.4.25/net/ipv6/Makefile 2004-06-26 11:29:30.000000000 +0100 -@@ -6,18 +6,28 @@ - # unless it's something special (ie not a .c file). - # - -+export-objs := ipv6_syms.o ipv6_tunnel.o - --O_TARGET := ipv6.o -+#list-multi := ipv6.o ipv6_tunnel.o - --obj-y := af_inet6.o anycast.o ip6_output.o ip6_input.o addrconf.o sit.o \ -- route.o ip6_fib.o ipv6_sockglue.o ndisc.o udp.o raw.o \ -- protocol.o icmp.o mcast.o reassembly.o tcp_ipv6.o \ -- exthdrs.o sysctl_net_ipv6.o datagram.o proc.o \ -- ip6_flowlabel.o ipv6_syms.o -+ipv6-objs := af_inet6.o anycast.o ip6_output.o ip6_input.o addrconf.o \ -+ sit.o route.o ip6_fib.o ipv6_sockglue.o ndisc.o udp.o \ -+ raw.o protocol.o icmp.o mcast.o reassembly.o tcp_ipv6.o \ -+ exthdrs.o sysctl_net_ipv6.o datagram.o proc.o \ -+ ip6_flowlabel.o ipv6_syms.o -+ -+ifneq ($(CONFIG_IPV6_MOBILITY),n) -+ifneq ($(CONFIG_IPV6_MOBILITY),) -+ipv6-objs += mipglue.o -+endif -+endif - --export-objs := ipv6_syms.o -+obj-$(CONFIG_IPV6) += ipv6.o -+obj-$(CONFIG_IPV6_TUNNEL) += ipv6_tunnel.o -+ -+ipv6.o: $(ipv6-objs) -+ $(LD) -r -o $@ $(ipv6-objs) - --obj-m := $(O_TARGET) - - #obj-$(CONFIG_IPV6_FIREWALL) += ip6_fw.o - -diff -uprN linux-2.4.25.old/net/ipv6/addrconf.c linux-2.4.25/net/ipv6/addrconf.c ---- linux-2.4.25.old/net/ipv6/addrconf.c 2003-11-28 18:26:21.000000000 +0000 -+++ linux-2.4.25/net/ipv6/addrconf.c 2004-06-26 11:29:30.000000000 +0100 -@@ -68,6 +68,8 @@ - - #include <asm/uaccess.h> - -+#include <net/mipglue.h> -+ - #define IPV6_MAX_ADDRESSES 16 - - /* Set to 3 to get tracing... */ -@@ -103,9 +105,9 @@ static spinlock_t addrconf_verify_lock = - - static int addrconf_ifdown(struct net_device *dev, int how); - --static void addrconf_dad_start(struct inet6_ifaddr *ifp, int flags); -+void addrconf_dad_start(struct inet6_ifaddr *ifp, int flags); - static void addrconf_dad_timer(unsigned long data); --static void addrconf_dad_completed(struct inet6_ifaddr *ifp); -+void addrconf_dad_completed(struct inet6_ifaddr *ifp); - static void addrconf_rs_timer(unsigned long data); - static void ipv6_ifa_notify(int event, struct inet6_ifaddr *ifa); - -@@ -330,38 +332,6 @@ static struct inet6_dev * ipv6_find_idev - return idev; - } - --void ipv6_addr_prefix(struct in6_addr *prefix, -- struct in6_addr *addr, int prefix_len) --{ -- unsigned long mask; -- int ncopy, nbits; -- -- memset(prefix, 0, sizeof(*prefix)); -- -- if (prefix_len <= 0) -- return; -- if (prefix_len > 128) -- prefix_len = 128; -- -- ncopy = prefix_len / 32; -- switch (ncopy) { -- case 4: prefix->s6_addr32[3] = addr->s6_addr32[3]; -- case 3: prefix->s6_addr32[2] = addr->s6_addr32[2]; -- case 2: prefix->s6_addr32[1] = addr->s6_addr32[1]; -- case 1: prefix->s6_addr32[0] = addr->s6_addr32[0]; -- case 0: break; -- } -- nbits = prefix_len % 32; -- if (nbits == 0) -- return; -- -- mask = ~((1 << (32 - nbits)) - 1); -- mask = htonl(mask); -- -- prefix->s6_addr32[ncopy] = addr->s6_addr32[ncopy] & mask; --} -- -- - static void dev_forward_change(struct inet6_dev *idev) - { - struct net_device *dev; -@@ -513,7 +483,7 @@ ipv6_add_addr(struct inet6_dev *idev, co - - /* This function wants to get referenced ifp and releases it before return */ - --static void ipv6_del_addr(struct inet6_ifaddr *ifp) -+void ipv6_del_addr(struct inet6_ifaddr *ifp) - { - struct inet6_ifaddr *ifa, **ifap; - struct inet6_dev *idev = ifp->idev; -@@ -662,6 +632,12 @@ out: - if (match) - in6_ifa_put(match); - -+ /* The home address is always used as source address in -+ * MIPL mobile IPv6 -+ */ -+ if (scope != IFA_HOST && scope != IFA_LINK) -+ addrconf_get_mipv6_home_address(saddr); -+ - return err; - } - -@@ -815,7 +791,7 @@ void addrconf_leave_solict(struct net_de - } - - --static int ipv6_generate_eui64(u8 *eui, struct net_device *dev) -+int ipv6_generate_eui64(u8 *eui, struct net_device *dev) - { - switch (dev->type) { - case ARPHRD_ETHER: -@@ -840,7 +816,7 @@ static int ipv6_generate_eui64(u8 *eui, - return -1; - } - --static int ipv6_inherit_eui64(u8 *eui, struct inet6_dev *idev) -+int ipv6_inherit_eui64(u8 *eui, struct inet6_dev *idev) - { - int err = -1; - struct inet6_ifaddr *ifp; -@@ -1407,6 +1383,24 @@ static void addrconf_sit_config(struct n - sit_route_add(dev); - } - -+/** -+ * addrconf_ipv6_tunnel_config - configure IPv6 tunnel device -+ * @dev: tunnel device -+ **/ -+ -+static void addrconf_ipv6_tunnel_config(struct net_device *dev) -+{ -+ struct inet6_dev *idev; -+ -+ ASSERT_RTNL(); -+ -+ /* Assign inet6_dev structure to tunnel device */ -+ if ((idev = ipv6_find_idev(dev)) == NULL) { -+ printk(KERN_DEBUG "init ipv6 tunnel: add_dev failed\n"); -+ return; -+ } -+} -+ - - int addrconf_notify(struct notifier_block *this, unsigned long event, - void * data) -@@ -1421,6 +1415,10 @@ int addrconf_notify(struct notifier_bloc - addrconf_sit_config(dev); - break; - -+ case ARPHRD_TUNNEL6: -+ addrconf_ipv6_tunnel_config(dev); -+ break; -+ - case ARPHRD_LOOPBACK: - init_loopback(dev); - break; -@@ -1602,7 +1600,7 @@ out: - /* - * Duplicate Address Detection - */ --static void addrconf_dad_start(struct inet6_ifaddr *ifp, int flags) -+void addrconf_dad_start(struct inet6_ifaddr *ifp, int flags) - { - struct net_device *dev; - unsigned long rand_num; -@@ -1667,7 +1665,7 @@ static void addrconf_dad_timer(unsigned - in6_ifa_put(ifp); - } - --static void addrconf_dad_completed(struct inet6_ifaddr *ifp) -+void addrconf_dad_completed(struct inet6_ifaddr *ifp) - { - struct net_device * dev = ifp->idev->dev; - -@@ -1676,7 +1674,7 @@ static void addrconf_dad_completed(struc - */ - - ipv6_ifa_notify(RTM_NEWADDR, ifp); -- -+ notifier_call_chain(&inet6addr_chain,NETDEV_UP,ifp); - /* If added prefix is link local and forwarding is off, - start sending router solicitations. - */ -@@ -1877,8 +1875,20 @@ inet6_rtm_newaddr(struct sk_buff *skb, s - if (rta[IFA_LOCAL-1]) { - if (pfx && memcmp(pfx, RTA_DATA(rta[IFA_LOCAL-1]), sizeof(*pfx))) - return -EINVAL; -+ if (ifm->ifa_flags & IFA_F_HOMEADDR && !rta[IFA_HOMEAGENT-1]) -+ return -EINVAL; - pfx = RTA_DATA(rta[IFA_LOCAL-1]); - } -+ if (rta[IFA_HOMEAGENT-1]) { -+ struct in6_addr *ha; -+ if (pfx == NULL || !(ifm->ifa_flags & IFA_F_HOMEADDR)) -+ return -EINVAL; -+ if (RTA_PAYLOAD(rta[IFA_HOMEAGENT-1]) < sizeof(*ha)) -+ return -EINVAL; -+ ha = RTA_DATA(rta[IFA_HOMEAGENT-1]); -+ addrconf_set_mipv6_mn_home(ifm->ifa_index, pfx, ifm->ifa_prefixlen, -+ ha, ifm->ifa_prefixlen); -+ } - if (pfx == NULL) - return -EINVAL; - -diff -uprN linux-2.4.25.old/net/ipv6/af_inet6.c linux-2.4.25/net/ipv6/af_inet6.c ---- linux-2.4.25.old/net/ipv6/af_inet6.c 2003-11-28 18:26:21.000000000 +0000 -+++ linux-2.4.25/net/ipv6/af_inet6.c 2004-06-26 11:29:30.000000000 +0100 -@@ -58,6 +58,9 @@ - #include <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 @@ static int __init inet6_init(void) - err = ndisc_init(&inet6_family_ops); - if (err) - goto ndisc_fail; -+#ifdef CONFIG_IPV6_TUNNEL -+ err = ip6_tunnel_init(); -+ if (err) -+ goto ip6_tunnel_fail; -+#endif - err = igmp6_init(&inet6_family_ops); - if (err) - goto igmp_fail; -@@ -698,6 +706,10 @@ proc_raw6_fail: - #endif - igmp_fail: - ndisc_cleanup(); -+#ifdef CONFIG_IPV6_TUNNEL -+ ip6_tunnel_cleanup(); -+ip6_tunnel_fail: -+#endif - ndisc_fail: - icmpv6_cleanup(); - icmp_fail: -@@ -730,6 +742,9 @@ static void inet6_exit(void) - ip6_route_cleanup(); - ipv6_packet_cleanup(); - igmp6_cleanup(); -+#ifdef CONFIG_IPV6_TUNNEL -+ ip6_tunnel_cleanup(); -+#endif - ndisc_cleanup(); - icmpv6_cleanup(); - #ifdef CONFIG_SYSCTL -diff -uprN linux-2.4.25.old/net/ipv6/exthdrs.c linux-2.4.25/net/ipv6/exthdrs.c ---- linux-2.4.25.old/net/ipv6/exthdrs.c 2003-08-25 12:44:44.000000000 +0100 -+++ linux-2.4.25/net/ipv6/exthdrs.c 2004-06-26 11:29:30.000000000 +0100 -@@ -41,6 +41,9 @@ - #include <net/ip6_route.h> - #include <net/addrconf.h> - -+#include <net/mipglue.h> -+#include <net/mipv6.h> -+ - #include <asm/uaccess.h> - - /* -@@ -160,7 +163,8 @@ bad: - *****************************/ - - struct tlvtype_proc tlvprocdestopt_lst[] = { -- /* No destination options are defined now */ -+ /* Mobility Support destination options */ -+ {MIPV6_TLV_HOMEADDR, mipv6_handle_dstopt}, - {-1, NULL} - }; - -@@ -210,6 +214,7 @@ static int ipv6_routing_header(struct sk - - struct ipv6_rt_hdr *hdr; - struct rt0_hdr *rthdr; -+ struct rt2_hdr *rt2hdr; - - if (!pskb_may_pull(skb, (skb->h.raw-skb->data)+8) || - !pskb_may_pull(skb, (skb->h.raw-skb->data)+((skb->h.raw[1]+1)<<3))) { -@@ -225,17 +230,25 @@ static int ipv6_routing_header(struct sk - kfree_skb(skb); - return -1; - } -- -+ /* Silently discard invalid packets containing RTH type 2 */ -+ if (hdr->type == IPV6_SRCRT_TYPE_2 && -+ (hdr->hdrlen != 2 || hdr->segments_left != 1)) { -+ kfree_skb(skb); -+ return -1; -+ } - looped_back: - if (hdr->segments_left == 0) { -- opt->srcrt = skb->h.raw - skb->nh.raw; -+ if (hdr->type == IPV6_SRCRT_TYPE_0) -+ opt->srcrt = skb->h.raw - skb->nh.raw; -+ else if (hdr->type == IPV6_SRCRT_TYPE_2) -+ opt->srcrt2 = skb->h.raw - skb->nh.raw; - skb->h.raw += (hdr->hdrlen + 1) << 3; - opt->dst0 = opt->dst1; - opt->dst1 = 0; - return (&hdr->nexthdr) - skb->nh.raw; - } - -- if (hdr->type != IPV6_SRCRT_TYPE_0) { -+ if (hdr->type != IPV6_SRCRT_TYPE_0 && hdr->type != IPV6_SRCRT_TYPE_2) { - icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, (&hdr->type) - skb->nh.raw); - return -1; - } -@@ -275,9 +288,20 @@ looped_back: - - i = n - --hdr->segments_left; - -- rthdr = (struct rt0_hdr *) hdr; -- addr = rthdr->addr; -- addr += i - 1; -+ if (hdr->type == IPV6_SRCRT_TYPE_0) { -+ rthdr = (struct rt0_hdr *) hdr; -+ addr = rthdr->addr; -+ addr += i - 1; -+ } else { -+ /* check that address is this node's home address */ -+ rt2hdr = (struct rt2_hdr *) hdr; -+ addr = &rt2hdr->addr; -+ if (!ipv6_chk_addr(addr, NULL) || -+ !ipv6_chk_mip_home_addr(addr)) { -+ kfree_skb(skb); -+ return -1; -+ } -+ } - - addr_type = ipv6_addr_type(addr); - -@@ -330,6 +354,10 @@ looped_back: - temporary (or permanent) backdoor. - If listening socket set IPV6_RTHDR to 2, then we invert header. - --ANK (980729) -+ -+ By the Mobile IPv6 specification Type 2 routing header MUST NOT be -+ inverted. -+ --AJT (20020917) - */ - - struct ipv6_txoptions * -@@ -352,6 +380,18 @@ ipv6_invert_rthdr(struct sock *sk, struc - struct ipv6_txoptions *opt; - int hdrlen = ipv6_optlen(hdr); - -+ if (hdr->type == IPV6_SRCRT_TYPE_2) { -+ opt = sock_kmalloc(sk, sizeof(*opt) + hdrlen, GFP_ATOMIC); -+ if (opt == NULL) -+ return NULL; -+ memset(opt, 0, sizeof(*opt)); -+ opt->tot_len = sizeof(*opt) + hdrlen; -+ opt->srcrt = (void*)(opt+1); -+ opt->opt_nflen = hdrlen; -+ memcpy(opt->srcrt, hdr, sizeof(struct rt2_hdr)); -+ return opt; -+ } -+ - if (hdr->segments_left || - hdr->type != IPV6_SRCRT_TYPE_0 || - hdr->hdrlen & 0x01) -@@ -622,8 +662,18 @@ u8 *ipv6_build_nfrag_opts(struct sk_buff - if (opt) { - if (opt->dst0opt) - prev_hdr = ipv6_build_exthdr(skb, prev_hdr, NEXTHDR_DEST, opt->dst0opt); -- if (opt->srcrt) -- prev_hdr = ipv6_build_rthdr(skb, prev_hdr, opt->srcrt, daddr); -+ if (opt->srcrt) { -+ if (opt->srcrt2) { -+ struct in6_addr *rt2_hop = &((struct rt2_hdr *)opt->srcrt2)->addr; -+ prev_hdr = ipv6_build_rthdr(skb, prev_hdr, opt->srcrt, rt2_hop); -+ } else -+ prev_hdr = ipv6_build_rthdr(skb, prev_hdr, opt->srcrt, daddr); -+ } -+ if (opt->srcrt2) { -+ struct inet6_skb_parm *parm = (struct inet6_skb_parm *)skb->cb; -+ ipv6_addr_copy(&parm->hoa, daddr); -+ prev_hdr = ipv6_build_rthdr(skb, prev_hdr, opt->srcrt2, daddr); -+ } - } - return prev_hdr; - } -@@ -684,6 +734,11 @@ void ipv6_push_nfrag_opts(struct sk_buff - u8 *proto, - struct in6_addr **daddr) - { -+ if (opt->srcrt2) { -+ struct inet6_skb_parm *parm = (struct inet6_skb_parm *)skb->cb; -+ ipv6_addr_copy(&parm->hoa, *daddr); -+ ipv6_push_rthdr(skb, proto, opt->srcrt2, daddr); -+ } - if (opt->srcrt) - ipv6_push_rthdr(skb, proto, opt->srcrt, daddr); - if (opt->dst0opt) -@@ -719,6 +774,8 @@ ipv6_dup_options(struct sock *sk, struct - *((char**)&opt2->auth) += dif; - if (opt2->srcrt) - *((char**)&opt2->srcrt) += dif; -+ if (opt2->srcrt2) -+ *((char**)&opt2->srcrt2) += dif; - } - return opt2; - } -diff -uprN linux-2.4.25.old/net/ipv6/icmp.c linux-2.4.25/net/ipv6/icmp.c ---- linux-2.4.25.old/net/ipv6/icmp.c 2003-11-28 18:26:21.000000000 +0000 -+++ linux-2.4.25/net/ipv6/icmp.c 2004-06-26 11:29:30.000000000 +0100 -@@ -61,6 +61,8 @@ - #include <net/addrconf.h> - #include <net/icmp.h> - -+#include <net/mipglue.h> -+ - #include <asm/uaccess.h> - #include <asm/system.h> - -@@ -364,6 +366,8 @@ void icmpv6_send(struct sk_buff *skb, in - - msg.len = len; - -+ icmpv6_swap_mipv6_addrs(skb); -+ - ip6_build_xmit(sk, icmpv6_getfrag, &msg, &fl, len, NULL, -1, - MSG_DONTWAIT); - if (type >= ICMPV6_DEST_UNREACH && type <= ICMPV6_PARAMPROB) -@@ -562,13 +566,13 @@ int icmpv6_rcv(struct sk_buff *skb) - rt6_pmtu_discovery(&orig_hdr->daddr, &orig_hdr->saddr, dev, - ntohl(hdr->icmp6_mtu)); - -- /* -- * Drop through to notify -- */ -+ icmpv6_notify(skb, type, hdr->icmp6_code, hdr->icmp6_mtu); -+ break; - - case ICMPV6_DEST_UNREACH: -- case ICMPV6_TIME_EXCEED: - case ICMPV6_PARAMPROB: -+ mipv6_icmp_rcv(skb); -+ case ICMPV6_TIME_EXCEED: - icmpv6_notify(skb, type, hdr->icmp6_code, hdr->icmp6_mtu); - break; - -@@ -597,6 +601,13 @@ int icmpv6_rcv(struct sk_buff *skb) - case ICMPV6_MGM_REDUCTION: - break; - -+ case MIPV6_DHAAD_REQUEST: -+ case MIPV6_DHAAD_REPLY: -+ case MIPV6_PREFIX_SOLICIT: -+ case MIPV6_PREFIX_ADV: -+ mipv6_icmp_rcv(skb); -+ break; -+ - default: - if (net_ratelimit()) - printk(KERN_DEBUG "icmpv6: msg of unkown type\n"); -diff -uprN linux-2.4.25.old/net/ipv6/ip6_fib.c linux-2.4.25/net/ipv6/ip6_fib.c ---- linux-2.4.25.old/net/ipv6/ip6_fib.c 2003-08-25 12:44:44.000000000 +0100 -+++ linux-2.4.25/net/ipv6/ip6_fib.c 2004-06-26 11:29:30.000000000 +0100 -@@ -18,6 +18,7 @@ - * Yuji SEKIYA @USAGI: Support default route on router node; - * remove ip6_null_entry from the top of - * routing table. -+ * Ville Nuorvala: Fixes to source address based routing - */ - #include <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 @@ static __inline__ void fib6_start_gc(str - mod_timer(&ip6_fib_timer, jiffies + ip6_rt_gc_interval); - } - -+static struct rt6_info * fib6_find_prefix(struct fib6_node *fn); -+ - /* - * Add routing information to the routing tree. - * <destination addr>/<source addr> -@@ -508,17 +510,19 @@ static __inline__ void fib6_start_gc(str - - int fib6_add(struct fib6_node *root, struct rt6_info *rt, struct nlmsghdr *nlh) - { -- struct fib6_node *fn; -+ struct fib6_node *fn = root; - int err = -ENOMEM; - -- fn = fib6_add_1(root, &rt->rt6i_dst.addr, sizeof(struct in6_addr), -- rt->rt6i_dst.plen, (u8*) &rt->rt6i_dst - (u8*) rt); -+#ifdef CONFIG_IPV6_SUBTREES -+ struct fib6_node *pn = NULL; - -+ fn = fib6_add_1(root, &rt->rt6i_src.addr, sizeof(struct in6_addr), -+ rt->rt6i_src.plen, (u8*) &rt->rt6i_src - (u8*) rt); -+ - if (fn == NULL) - goto out; - --#ifdef CONFIG_IPV6_SUBTREES -- if (rt->rt6i_src.plen) { -+ if (rt->rt6i_dst.plen) { - struct fib6_node *sn; - - if (fn->subtree == NULL) { -@@ -546,9 +550,9 @@ int fib6_add(struct fib6_node *root, str - - /* Now add the first leaf node to new subtree */ - -- sn = fib6_add_1(sfn, &rt->rt6i_src.addr, -- sizeof(struct in6_addr), rt->rt6i_src.plen, -- (u8*) &rt->rt6i_src - (u8*) rt); -+ sn = fib6_add_1(sfn, &rt->rt6i_dst.addr, -+ sizeof(struct in6_addr), rt->rt6i_dst.plen, -+ (u8*) &rt->rt6i_dst - (u8*) rt); - - if (sn == NULL) { - /* If it is failed, discard just allocated -@@ -562,21 +566,30 @@ int fib6_add(struct fib6_node *root, str - /* Now link new subtree to main tree */ - sfn->parent = fn; - fn->subtree = sfn; -- if (fn->leaf == NULL) { -- fn->leaf = rt; -- atomic_inc(&rt->rt6i_ref); -- } - } else { -- sn = fib6_add_1(fn->subtree, &rt->rt6i_src.addr, -- sizeof(struct in6_addr), rt->rt6i_src.plen, -- (u8*) &rt->rt6i_src - (u8*) rt); -+ sn = fib6_add_1(fn->subtree, &rt->rt6i_dst.addr, -+ sizeof(struct in6_addr), rt->rt6i_dst.plen, -+ (u8*) &rt->rt6i_dst - (u8*) rt); - - if (sn == NULL) - goto st_failure; - } - -+ /* fib6_add_1 might have cleared the old leaf pointer */ -+ if (fn->leaf == NULL) { -+ fn->leaf = rt; -+ atomic_inc(&rt->rt6i_ref); -+ } -+ -+ pn = fn; - fn = sn; - } -+#else -+ fn = fib6_add_1(root, &rt->rt6i_dst.addr, sizeof(struct in6_addr), -+ rt->rt6i_dst.plen, (u8*) &rt->rt6i_dst - (u8*) rt); -+ -+ if (fn == NULL) -+ goto out; - #endif - - err = fib6_add_rt2node(fn, rt, nlh); -@@ -588,8 +601,30 @@ int fib6_add(struct fib6_node *root, str - } - - out: -- if (err) -+ if (err) { -+#ifdef CONFIG_IPV6_SUBTREES -+ -+ /* If fib6_add_1 has cleared the old leaf pointer in the -+ super-tree leaf node, we have to find a new one for it. -+ -+ This situation will never arise in the sub-tree since -+ the node will at least have the route that caused -+ fib6_add_rt2node to fail. -+ */ -+ -+ if (pn && !(pn->fn_flags & RTN_RTINFO)) { -+ pn->leaf = fib6_find_prefix(pn); -+#if RT6_DEBUG >= 2 -+ if (!pn->leaf) { -+ BUG_TRAP(pn->leaf); -+ pn->leaf = &ip6_null_entry; -+ } -+#endif -+ atomic_inc(&pn->leaf->rt6i_ref); -+ } -+#endif - dst_free(&rt->u.dst); -+ } - return err; - - #ifdef CONFIG_IPV6_SUBTREES -@@ -597,8 +632,8 @@ out: - is orphan. If it is, shoot it. - */ - st_failure: -- if (fn && !(fn->fn_flags&RTN_RTINFO|RTN_ROOT)) -- fib_repair_tree(fn); -+ if (fn && !(fn->fn_flags & (RTN_RTINFO | RTN_ROOT))) -+ fib6_repair_tree(fn); - dst_free(&rt->u.dst); - return err; - #endif -@@ -641,22 +676,28 @@ static struct fib6_node * fib6_lookup_1( - break; - } - -- while ((fn->fn_flags & RTN_ROOT) == 0) { -+ for (;;) { - #ifdef CONFIG_IPV6_SUBTREES - if (fn->subtree) { -- struct fib6_node *st; -- struct lookup_args *narg; -- -- narg = args + 1; -- -- if (narg->addr) { -- st = fib6_lookup_1(fn->subtree, narg); -+ struct rt6key *key; - -- if (st && !(st->fn_flags & RTN_ROOT)) -- return st; -+ key = (struct rt6key *) ((u8 *) fn->leaf + -+ args->offset); -+ -+ if (addr_match(&key->addr, args->addr, key->plen)) { -+ struct fib6_node *st; -+ struct lookup_args *narg = args + 1; -+ if (!ipv6_addr_any(narg->addr)) { -+ st = fib6_lookup_1(fn->subtree, narg); -+ -+ if (st && !(st->fn_flags & RTN_ROOT)) -+ return st; -+ } - } - } - #endif -+ if (fn->fn_flags & RTN_ROOT) -+ break; - - if (fn->fn_flags & RTN_RTINFO) { - struct rt6key *key; -@@ -680,13 +721,22 @@ struct fib6_node * fib6_lookup(struct fi - struct lookup_args args[2]; - struct rt6_info *rt = NULL; - struct fib6_node *fn; -+#ifdef CONFIG_IPV6_SUBTREES -+ struct in6_addr saddr_buf; -+#endif - -+#ifdef CONFIG_IPV6_SUBTREES -+ if (saddr == NULL) { -+ memset(&saddr_buf, 0, sizeof(struct in6_addr)); -+ saddr = &saddr_buf; -+ } -+ args[0].offset = (u8*) &rt->rt6i_src - (u8*) rt; -+ args[0].addr = saddr; -+ args[1].offset = (u8*) &rt->rt6i_dst - (u8*) rt; -+ args[1].addr = daddr; -+#else - args[0].offset = (u8*) &rt->rt6i_dst - (u8*) rt; - args[0].addr = daddr; -- --#ifdef CONFIG_IPV6_SUBTREES -- args[1].offset = (u8*) &rt->rt6i_src - (u8*) rt; -- args[1].addr = saddr; - #endif - - fn = fib6_lookup_1(root, args); -@@ -739,19 +789,25 @@ struct fib6_node * fib6_locate(struct fi - { - struct rt6_info *rt = NULL; - struct fib6_node *fn; -- -- fn = fib6_locate_1(root, daddr, dst_len, -- (u8*) &rt->rt6i_dst - (u8*) rt); -- - #ifdef CONFIG_IPV6_SUBTREES -- if (src_len) { -- BUG_TRAP(saddr!=NULL); -- if (fn == NULL) -- fn = fn->subtree; -+ struct in6_addr saddr_buf; -+ -+ if (saddr == NULL) { -+ memset(&saddr_buf, 0, sizeof(struct in6_addr)); -+ saddr = &saddr_buf; -+ } -+ fn = fib6_locate_1(root, saddr, src_len, -+ (u8*) &rt->rt6i_src - (u8*) rt); -+ if (dst_len) { - if (fn) -- fn = fib6_locate_1(fn, saddr, src_len, -- (u8*) &rt->rt6i_src - (u8*) rt); -+ fn = fib6_locate_1(fn->subtree, daddr, dst_len, -+ (u8*) &rt->rt6i_dst - (u8*) rt); -+ else -+ return NULL; - } -+#else -+ fn = fib6_locate_1(root, daddr, dst_len, -+ (u8*) &rt->rt6i_dst - (u8*) rt); - #endif - - if (fn && fn->fn_flags&RTN_RTINFO) -@@ -939,7 +995,7 @@ static void fib6_del_route(struct fib6_n - } - fn = fn->parent; - } -- /* No more references are possiible at this point. */ -+ /* No more references are possible at this point. */ - if (atomic_read(&rt->rt6i_ref) != 1) BUG(); - } - -diff -uprN linux-2.4.25.old/net/ipv6/ip6_input.c linux-2.4.25/net/ipv6/ip6_input.c ---- linux-2.4.25.old/net/ipv6/ip6_input.c 2003-08-25 12:44:44.000000000 +0100 -+++ linux-2.4.25/net/ipv6/ip6_input.c 2004-06-26 11:29:30.000000000 +0100 -@@ -40,13 +40,42 @@ - #include <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); - } - -diff -uprN linux-2.4.25.old/net/ipv6/ip6_output.c linux-2.4.25/net/ipv6/ip6_output.c ---- linux-2.4.25.old/net/ipv6/ip6_output.c 2003-08-25 12:44:44.000000000 +0100 -+++ linux-2.4.25/net/ipv6/ip6_output.c 2004-06-26 11:29:30.000000000 +0100 -@@ -50,6 +50,8 @@ - #include <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 @@ int ip6_xmit(struct sock *sk, struct sk_ - u8 proto = fl->proto; - int seg_len = skb->len; - int hlimit; -+ int retval; -+ struct ipv6_txoptions *orig_opt = opt; -+ -+ opt = ip6_add_mipv6_txoptions(sk, skb, orig_opt, fl, &dst); - -+ if(orig_opt && !opt) -+ return -ENOMEM; -+ - if (opt) { - int head_room; - -@@ -209,8 +218,11 @@ int ip6_xmit(struct sock *sk, struct sk_ - struct sk_buff *skb2 = skb_realloc_headroom(skb, head_room); - kfree_skb(skb); - skb = skb2; -- if (skb == NULL) -+ if (skb == NULL) { -+ ip6_free_mipv6_txoptions(opt, orig_opt); -+ - return -ENOBUFS; -+ } - if (sk) - skb_set_owner_w(skb, sk); - } -@@ -242,7 +254,10 @@ int ip6_xmit(struct sock *sk, struct sk_ - - if (skb->len <= dst->pmtu) { - IP6_INC_STATS(Ip6OutRequests); -- return NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, dst->dev, ip6_maybe_reroute); -+ ip6_mark_mipv6_packet(opt, skb); -+ retval = NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, dst->dev, ip6_maybe_reroute); -+ ip6_free_mipv6_txoptions(opt, orig_opt); -+ return retval; - } - - if (net_ratelimit()) -@@ -250,6 +265,9 @@ int ip6_xmit(struct sock *sk, struct sk_ - skb->dev = dst->dev; - icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, dst->pmtu, skb->dev); - kfree_skb(skb); -+ -+ ip6_free_mipv6_txoptions(opt, orig_opt); -+ - return -EMSGSIZE; - } - -@@ -473,6 +491,7 @@ static int ip6_frag_xmit(struct sock *sk - - IP6_INC_STATS(Ip6FragCreates); - IP6_INC_STATS(Ip6OutRequests); -+ ip6_mark_mipv6_packet(opt, skb); - err = NF_HOOK(PF_INET6,NF_IP6_LOCAL_OUT, skb, NULL, dst->dev, ip6_maybe_reroute); - if (err) { - kfree_skb(last_skb); -@@ -499,6 +518,7 @@ static int ip6_frag_xmit(struct sock *sk - IP6_INC_STATS(Ip6FragCreates); - IP6_INC_STATS(Ip6FragOKs); - IP6_INC_STATS(Ip6OutRequests); -+ ip6_mark_mipv6_packet(opt, last_skb); - return NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, last_skb, NULL,dst->dev, ip6_maybe_reroute); - } - -@@ -509,26 +529,43 @@ int ip6_build_xmit(struct sock *sk, inet - struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6; - struct in6_addr *final_dst = NULL; - struct dst_entry *dst; -+ struct rt6_info *rt; - int err = 0; - unsigned int pktlength, jumbolen, mtu; - struct in6_addr saddr; -+ struct ipv6_txoptions *orig_opt = opt; -+#ifdef CONFIG_IPV6_SUBTREES -+ struct dst_entry *org_dst; -+#endif -+ -+ opt = ip6_add_mipv6_txoptions(sk, NULL, orig_opt, fl, NULL); -+ -+ if(orig_opt && !opt) -+ return -ENOMEM; - - if (opt && opt->srcrt) { - struct rt0_hdr *rt0 = (struct rt0_hdr *) opt->srcrt; - final_dst = fl->fl6_dst; - fl->fl6_dst = rt0->addr; -- } -+ } else if (opt && opt->srcrt2) { -+ struct rt2_hdr *rt2 = (struct rt2_hdr *) opt->srcrt2; -+ final_dst = fl->fl6_dst; -+ fl->fl6_dst = &rt2->addr; -+ } - - if (!fl->oif && ipv6_addr_is_multicast(fl->nl_u.ip6_u.daddr)) - fl->oif = np->mcast_oif; - - dst = __sk_dst_check(sk, np->dst_cookie); -+#ifdef CONFIG_IPV6_SUBTREES -+ org_dst = dst; -+#endif - if (dst) { -- struct rt6_info *rt = (struct rt6_info*)dst; -+ rt = (struct rt6_info*)dst; - - /* Yes, checking route validity in not connected - case is not very simple. Take into account, -- that we do not support routing by source, TOS, -+ that we do not support routing by TOS, - and MSG_DONTROUTE --ANK (980726) - - 1. If route was host route, check that -@@ -548,6 +585,13 @@ int ip6_build_xmit(struct sock *sk, inet - ipv6_addr_cmp(fl->fl6_dst, &rt->rt6i_dst.addr)) - && (np->daddr_cache == NULL || - ipv6_addr_cmp(fl->fl6_dst, np->daddr_cache))) -+#ifdef CONFIG_IPV6_SUBTREES -+ || (fl->fl6_src != NULL -+ && (rt->rt6i_src.plen != 128 || -+ ipv6_addr_cmp(fl->fl6_src, &rt->rt6i_src.addr)) -+ && (np->saddr_cache == NULL || -+ ipv6_addr_cmp(fl->fl6_src, np->saddr_cache))) -+#endif - || (fl->oif && fl->oif != dst->dev->ifindex)) { - dst = NULL; - } else -@@ -560,21 +604,42 @@ int ip6_build_xmit(struct sock *sk, inet - if (dst->error) { - IP6_INC_STATS(Ip6OutNoRoutes); - dst_release(dst); -+ ip6_free_mipv6_txoptions(opt, orig_opt); - return -ENETUNREACH; - } - - if (fl->fl6_src == NULL) { - err = ipv6_get_saddr(dst, fl->fl6_dst, &saddr); -- - if (err) { - #if IP6_DEBUG >= 2 - printk(KERN_DEBUG "ip6_build_xmit: " - "no available source address\n"); - #endif -+ -+#ifdef CONFIG_IPV6_SUBTREES -+ if (dst != org_dst) { -+ dst_release(dst); -+ dst = org_dst; -+ } -+#endif - goto out; - } - fl->fl6_src = &saddr; - } -+#ifdef CONFIG_IPV6_SUBTREES -+ rt = (struct rt6_info*)dst; -+ if (dst != org_dst || rt->rt6i_src.plen != 128 || -+ ipv6_addr_cmp(fl->fl6_src, &rt->rt6i_src.addr)) { -+ dst_release(dst); -+ dst = ip6_route_output(sk, fl); -+ if (dst->error) { -+ IP6_INC_STATS(Ip6OutNoRoutes); -+ dst_release(dst); -+ ip6_free_mipv6_txoptions(opt, orig_opt); -+ return -ENETUNREACH; -+ } -+ } -+#endif - pktlength = length; - - if (hlimit < 0) { -@@ -667,6 +732,7 @@ int ip6_build_xmit(struct sock *sk, inet - - if (!err) { - IP6_INC_STATS(Ip6OutRequests); -+ ip6_mark_mipv6_packet(opt, skb); - err = NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, dst->dev, ip6_maybe_reroute); - } else { - err = -EFAULT; -@@ -688,9 +754,14 @@ int ip6_build_xmit(struct sock *sk, inet - * cleanup - */ - out: -- ip6_dst_store(sk, dst, fl->nl_u.ip6_u.daddr == &np->daddr ? &np->daddr : NULL); -+ ip6_dst_store(sk, dst, -+ fl->nl_u.ip6_u.daddr == &np->daddr ? &np->daddr : NULL, -+ fl->nl_u.ip6_u.saddr == &np->saddr ? &np->saddr : NULL); - if (err > 0) - err = np->recverr ? net_xmit_errno(err) : 0; -+ -+ ip6_free_mipv6_txoptions(opt, orig_opt); -+ - return err; - } - -@@ -769,6 +840,15 @@ int ip6_forward(struct sk_buff *skb) - return -ETIMEDOUT; - } - -+ /* The proxying router can't forward traffic sent to a link-local -+ address, so signal the sender and discard the packet. This -+ behavior is required by the MIPv6 specification. */ -+ -+ if (ipv6_addr_type(&hdr->daddr) & IPV6_ADDR_LINKLOCAL && -+ skb->dev && pneigh_lookup(&nd_tbl, &hdr->daddr, skb->dev, 0)) { -+ dst_link_failure(skb); -+ goto drop; -+ } - /* IPv6 specs say nothing about it, but it is clear that we cannot - send redirects to source routed frames. - */ -diff -uprN linux-2.4.25.old/net/ipv6/ipv6_syms.c linux-2.4.25/net/ipv6/ipv6_syms.c ---- linux-2.4.25.old/net/ipv6/ipv6_syms.c 2003-11-28 18:26:21.000000000 +0000 -+++ linux-2.4.25/net/ipv6/ipv6_syms.c 2004-06-26 11:29:30.000000000 +0100 -@@ -6,6 +6,8 @@ - #include <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); -@@ -33,3 +35,48 @@ EXPORT_SYMBOL(inet6_ioctl); - EXPORT_SYMBOL(ipv6_get_saddr); - EXPORT_SYMBOL(ipv6_chk_addr); - EXPORT_SYMBOL(in6_dev_finish_destroy); -+ -+#if defined(CONFIG_IPV6_TUNNEL_MODULE) || defined(CONFIG_IPV6_MOBILITY_MODULE) -+EXPORT_SYMBOL(ip6_build_xmit); -+EXPORT_SYMBOL(rt6_lookup); -+EXPORT_SYMBOL(ipv6_ext_hdr); -+#endif -+#ifdef CONFIG_IPV6_MOBILITY_MODULE -+EXPORT_SYMBOL(mipv6_functions); -+EXPORT_SYMBOL(mipv6_invalidate_calls); -+#if defined(CONFIG_IPV6_MOBILITY_HA_MODULE) || defined(CONFIG_IPV6_MOBILITY_MN_MODULE) -+EXPORT_SYMBOL(ip6_route_add); -+EXPORT_SYMBOL(ip6_route_del); -+EXPORT_SYMBOL(ipv6_get_lladdr); -+EXPORT_SYMBOL(ipv6_get_ifaddr); -+EXPORT_SYMBOL(nd_tbl); -+EXPORT_SYMBOL(ndisc_send_ns); -+EXPORT_SYMBOL(ndisc_send_na); -+EXPORT_SYMBOL(ndisc_next_option); -+EXPORT_SYMBOL(inet6_ifa_finish_destroy); -+#endif -+#ifdef CONFIG_IPV6_MOBILITY_HA_MODULE -+EXPORT_SYMBOL(ipv6_dev_ac_dec); -+EXPORT_SYMBOL(ipv6_dev_ac_inc); -+EXPORT_SYMBOL(ipv6_dev_mc_dec); -+EXPORT_SYMBOL(ipv6_dev_mc_inc); -+EXPORT_SYMBOL(ip6_forward); -+EXPORT_SYMBOL(ip6_input); -+EXPORT_SYMBOL(ipv6_chk_acast_addr); -+#endif -+#ifdef CONFIG_IPV6_MOBILITY_MN_MODULE -+#endif -+EXPORT_SYMBOL(addrconf_add_ifaddr); -+EXPORT_SYMBOL(addrconf_del_ifaddr); -+EXPORT_SYMBOL(addrconf_dad_start); -+EXPORT_SYMBOL(ip6_del_rt); -+EXPORT_SYMBOL(ip6_routing_table); -+EXPORT_SYMBOL(rt6_get_dflt_router); -+EXPORT_SYMBOL(rt6_purge_dflt_routers); -+EXPORT_SYMBOL(rt6_lock); -+EXPORT_SYMBOL(ndisc_send_rs); -+EXPORT_SYMBOL(fib6_clean_tree); -+EXPORT_SYMBOL(ipv6_del_addr); -+EXPORT_SYMBOL(ipv6_generate_eui64); -+EXPORT_SYMBOL(ipv6_inherit_eui64); -+#endif -diff -uprN linux-2.4.25.old/net/ipv6/ipv6_tunnel.c linux-2.4.25/net/ipv6/ipv6_tunnel.c ---- linux-2.4.25.old/net/ipv6/ipv6_tunnel.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/ipv6_tunnel.c 2004-06-26 11:29:30.000000000 +0100 -@@ -0,0 +1,1604 @@ -+/* -+ * IPv6 over IPv6 tunnel device -+ * Linux INET6 implementation -+ * -+ * Authors: -+ * Ville Nuorvala <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 -+ -diff -uprN linux-2.4.25.old/net/ipv6/mipglue.c linux-2.4.25/net/ipv6/mipglue.c ---- linux-2.4.25.old/net/ipv6/mipglue.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mipglue.c 2004-06-26 11:29:30.000000000 +0100 -@@ -0,0 +1,63 @@ -+/* -+ * Glue for Mobility support integration to IPv6 -+ * -+ * Authors: -+ * Antti Tuominen <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; -+} -+ -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/Config.in linux-2.4.25/net/ipv6/mobile_ip6/Config.in ---- linux-2.4.25.old/net/ipv6/mobile_ip6/Config.in 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/Config.in 2004-06-26 11:29:30.000000000 +0100 -@@ -0,0 +1,12 @@ -+# -+# Mobile IPv6 Configuration -+# -+dep_tristate ' IPv6: Mobility Support (Correspondent Node)' CONFIG_IPV6_MOBILITY $CONFIG_IPV6 -+if [ "$CONFIG_IPV6_IPV6_TUNNEL" != "n" ]; then -+ dep_tristate ' MIPv6: Mobile Node Support' CONFIG_IPV6_MOBILITY_MN $CONFIG_IPV6_MOBILITY -+ -+ dep_tristate ' MIPv6: Home Agent Support' CONFIG_IPV6_MOBILITY_HA $CONFIG_IPV6_MOBILITY -+fi -+if [ "$CONFIG_IPV6_MOBILITY" != "n" ]; then -+ bool ' MIPv6: Debug messages' CONFIG_IPV6_MOBILITY_DEBUG -+fi -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/Makefile linux-2.4.25/net/ipv6/mobile_ip6/Makefile ---- linux-2.4.25.old/net/ipv6/mobile_ip6/Makefile 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/Makefile 2004-06-26 11:29:30.000000000 +0100 -@@ -0,0 +1,35 @@ -+# -+# Makefile for the MIPL Mobile IPv6 for Linux. -+# -+# Note! Dependencies are done automagically by 'make dep', which also -+# removes any old dependencies. DON'T put your own dependencies here -+# unless it's something special (ie not a .c file). -+# -+ -+ -+O_TARGET := mip6_base.o -+ -+list-multi := mip6_ha.o mip6_mn.o -+ -+obj-y := hashlist.o bcache.o mobhdr_common.o stats.o exthdrs.o \ -+ rr_crypto.o hmac.o auth_opt.o mipv6_icmp.o module_cn.o -+ -+obj-m := $(O_TARGET) -+ -+mip6_ha-objs := halist.o mipv6_icmp_ha.o tunnel_ha.o \ -+ ndisc_ha.o ha.o module_ha.o -+ -+mip6_mn-objs := mipv6_icmp_mn.o ioctl_mn.o tunnel_mn.o \ -+ mdetect.o bul.o multiaccess_ctl.o mobhdr_mn.o mn.o \ -+ module_mn.o -+ -+obj-$(CONFIG_IPV6_MOBILITY_HA) += mip6_ha.o -+obj-$(CONFIG_IPV6_MOBILITY_MN) += mip6_mn.o -+ -+include $(TOPDIR)/Rules.make -+ -+mip6_ha.o: $(mip6_ha-objs) -+ $(LD) -r -o $@ $(mip6_ha-objs) -+ -+mip6_mn.o: $(mip6_mn-objs) -+ $(LD) -r -o $@ $(mip6_mn-objs) -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/README linux-2.4.25/net/ipv6/mobile_ip6/README ---- linux-2.4.25.old/net/ipv6/mobile_ip6/README 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/README 2004-06-26 11:29:30.000000000 +0100 -@@ -0,0 +1,15 @@ -+MIPL Mobile IPv6 for Linux -+ -+More information at http://www.mipl.mediapoli.com/. -+ -+To join MIPL Mobile IPv6 for Linux mailing lists go to: -+ -+ http://www.mipl.mediapoli.com/cgi-bin/mailman/listinfo -+ -+Or send mail with subject "subscribe" for the general list to: -+ -+ mipl-request@list.mipl.mediapoli.com -+ -+or for the developer list to: -+ -+ mipl-devel-request@list.mail.mediapoli.com -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/auth_opt.c linux-2.4.25/net/ipv6/mobile_ip6/auth_opt.c ---- linux-2.4.25.old/net/ipv6/mobile_ip6/auth_opt.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/auth_opt.c 2004-06-26 11:29:30.000000000 +0100 -@@ -0,0 +1,121 @@ -+/* -+ * MIPv6 Binding Authentication Data Option functions -+ * -+ * Authors: -+ * Henrik Petander <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; -+} -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/bcache.c linux-2.4.25/net/ipv6/mobile_ip6/bcache.c ---- linux-2.4.25.old/net/ipv6/mobile_ip6/bcache.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/bcache.c 2004-06-26 11:29:30.000000000 +0100 -@@ -0,0 +1,746 @@ -+/* -+ * Binding Cache -+ * -+ * Authors: -+ * Juha Mynttinen <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; -+} -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/bcache.h linux-2.4.25/net/ipv6/mobile_ip6/bcache.h ---- linux-2.4.25.old/net/ipv6/mobile_ip6/bcache.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/bcache.h 2004-06-26 11:29:30.000000000 +0100 -@@ -0,0 +1,72 @@ -+/* -+ * MIPL Mobile IPv6 Binding Cache header file -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#ifndef _BCACHE_H -+#define _BCACHE_H -+ -+#include <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 */ -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/bul.c linux-2.4.25/net/ipv6/mobile_ip6/bul.c ---- linux-2.4.25.old/net/ipv6/mobile_ip6/bul.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/bul.c 2004-06-26 11:29:30.000000000 +0100 -@@ -0,0 +1,634 @@ -+/* -+ * Binding update list -+ * -+ * Authors: -+ * Juha Mynttinen <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"); -+} -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/bul.h linux-2.4.25/net/ipv6/mobile_ip6/bul.h ---- linux-2.4.25.old/net/ipv6/mobile_ip6/bul.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/bul.h 2004-06-26 11:29:30.000000000 +0100 -@@ -0,0 +1,91 @@ -+/* -+ * MIPL Mobile IPv6 Binding Update List header file -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#ifndef _BUL_H -+#define _BUL_H -+ -+#include "hashlist.h" -+ -+#define ACK_OK 0x01 -+#define RESEND_EXP 0x02 -+#define ACK_ERROR 0x04 -+ -+#define HOME_RESEND_EXPIRE 3600 -+#define MIPV6_COOKIE_LEN 8 -+struct mipv6_rr_info { -+ /* RR information */ -+ u16 rr_state; /* State of the RR */ -+ u16 rr_flags; /* Flags for the RR */ -+ u8 hot_cookie[MIPV6_COOKIE_LEN]; /* HoT Cookie */ -+ u8 cot_cookie[MIPV6_COOKIE_LEN]; /* CoT Cookie */ -+ u8 home_cookie[MIPV6_COOKIE_LEN]; /* Home Cookie */ -+ u8 careof_cookie[MIPV6_COOKIE_LEN]; /* Careof Cookie */ -+ u32 lastsend_hoti; /* When HoTI was last sent (jiffies) */ -+ u32 lastsend_coti; /* When CoTI was last sent (jiffies) */ -+ u32 home_time; /* when Care-of cookie was received */ -+ u32 careof_time; /* when Home cookie was received */ -+ int home_nonce_index; /* Home cookie nonce index */ -+ int careof_nonce_index; /* Care-of cookie nonce index */ -+ u8 *kbu; /* Binding authentication key */ -+}; -+struct mipv6_bul_entry { -+ struct hashlist_entry e; -+ struct in6_addr cn_addr; /* CN to which BU was sent */ -+ struct in6_addr home_addr; /* home address of this binding */ -+ struct in6_addr coa; /* care-of address of the sent BU */ -+ -+ unsigned long expire; /* entry's expiration time (jiffies) */ -+ __u32 lifetime; /* lifetime sent in this BU */ -+ __u32 lastsend; /* last time when BU sent (jiffies) */ -+ __u32 consecutive_sends; /* Number of consecutive BU's sent */ -+ __u16 seq; /* sequence number of the latest BU */ -+ __u8 flags; /* BU send flags */ -+ __u8 state; /* resend state */ -+ __u32 initdelay; /* initial ack wait */ -+ __u32 delay; /* current ack wait */ -+ __u32 maxdelay; /* maximum ack wait */ -+ -+ struct mipv6_rr_info *rr; -+ struct mipv6_mh_opt *ops; /* saved option values */ -+ -+ unsigned long callback_time; -+ int (*callback)(struct mipv6_bul_entry *entry); -+}; -+ -+extern rwlock_t bul_lock; -+ -+int mipv6_bul_init(__u32 size); -+ -+void mipv6_bul_exit(void); -+ -+struct mipv6_bul_entry *mipv6_bul_add( -+ struct in6_addr *cn_addr, struct in6_addr *home_addr, -+ struct in6_addr *coa, __u32 lifetime, __u16 seq, __u8 flags, -+ int (*callback)(struct mipv6_bul_entry *entry), __u32 callback_time, -+ __u8 state, __u32 delay, __u32 maxdelay, struct mipv6_mh_opt *ops, -+ struct mipv6_rr_info *rr); -+ -+int mipv6_bul_delete(struct in6_addr *cn_addr, struct in6_addr *home_addr); -+ -+int mipv6_bul_exists(struct in6_addr *cnaddr, struct in6_addr *home_addr); -+ -+struct mipv6_bul_entry *mipv6_bul_get(struct in6_addr *cnaddr, -+ struct in6_addr *home_addr); -+struct mipv6_bul_entry *mipv6_bul_get_by_ccookie(struct in6_addr *cn_addr, -+ u8 *cookie); -+ -+int bul_entry_expired(struct mipv6_bul_entry *bulentry); -+ -+void mipv6_bul_reschedule(struct mipv6_bul_entry *entry); -+ -+int mipv6_bul_iterate(int (*func)(void *, void *, unsigned long *), void *args); -+ -+#endif /* BUL_H */ -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/config.h linux-2.4.25/net/ipv6/mobile_ip6/config.h ---- linux-2.4.25.old/net/ipv6/mobile_ip6/config.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/config.h 2004-06-26 11:29:30.000000000 +0100 -@@ -0,0 +1,72 @@ -+/* -+ * Configuration parameters -+ * -+ * $Id$ -+ */ -+ -+#define MIPV6VERSION "D24" -+#define MIPLVERSION "v1.0" -+ -+#define CAP_CN 0x01 -+#define CAP_HA 0x02 -+#define CAP_MN 0x04 -+ -+struct mip6_conf { -+ int capabilities; -+ int debug_level; -+ int accept_ret_rout; -+ int max_rtr_reachable_time; -+ int eager_cell_switching; -+ int max_num_tunnels; -+ int min_num_tunnels; -+ int binding_refresh_advice; -+ int bu_lladdr; -+ int bu_keymgm; -+ int bu_cn_ack; -+}; -+ -+extern struct mip6_conf mip6node_cnf; -+ -+struct mipv6_bce; -+ -+struct mip6_func { -+ void (*bce_home_add) (int ifindex, struct in6_addr *daddr, -+ struct in6_addr *haddr, struct in6_addr *coa, -+ struct in6_addr *rep_coa, __u32 lifetime, -+ __u16 sequence, __u8 flags, __u8 *k_bu); -+ void (*bce_cache_add) (int ifindex, struct in6_addr *daddr, -+ struct in6_addr *haddr, struct in6_addr *coa, -+ struct in6_addr *rep_coa, __u32 lifetime, -+ __u16 sequence, __u8 flags, __u8 *k_bu); -+ void (*bce_home_del) (struct in6_addr *daddr, struct in6_addr *haddr, -+ struct in6_addr *coa, struct in6_addr *rep_coa, -+ __u16 sequence, __u8 flags, -+ __u8 *k_bu); -+ void (*bce_cache_del) (struct in6_addr *daddr, struct in6_addr *haddr, -+ struct in6_addr *coa, struct in6_addr *rep_coa, -+ __u16 sequence, __u8 flags, -+ __u8 *k_bu); -+ -+ int (*bce_tnl_rt_add) (struct in6_addr *coa, -+ struct in6_addr *ha_addr, -+ struct in6_addr *home_addr); -+ -+ void (*bce_tnl_rt_del) (struct in6_addr *coa, -+ struct in6_addr *ha_addr, -+ struct in6_addr *home_addr); -+ -+ void (*proxy_del) (struct in6_addr *home_addr, struct mipv6_bce *entry); -+ int (*proxy_create) (int flags, int ifindex, struct in6_addr *coa, -+ struct in6_addr *our_addr, struct in6_addr *home_addr); -+ -+ int (*icmpv6_dhaad_rep_rcv) (struct sk_buff *skb); -+ int (*icmpv6_dhaad_req_rcv) (struct sk_buff *skb); -+ int (*icmpv6_pfxadv_rcv) (struct sk_buff *skb); -+ int (*icmpv6_pfxsol_rcv) (struct sk_buff *skb); -+ int (*icmpv6_paramprob_rcv) (struct sk_buff *skb); -+ -+ int (*mn_use_hao) (struct in6_addr *daddr, struct in6_addr *saddr); -+ void (*mn_check_tunneled_packet) (struct sk_buff *skb); -+}; -+ -+extern struct mip6_func mip6_fn; -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/debug.h linux-2.4.25/net/ipv6/mobile_ip6/debug.h ---- linux-2.4.25.old/net/ipv6/mobile_ip6/debug.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/debug.h 2004-06-26 11:29:30.000000000 +0100 -@@ -0,0 +1,112 @@ -+/* -+ * MIPL Mobile IPv6 Debugging macros and functions -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#ifndef _DEBUG_H -+#define _DEBUG_H -+ -+#include <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 */ -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/exthdrs.c linux-2.4.25/net/ipv6/mobile_ip6/exthdrs.c ---- linux-2.4.25.old/net/ipv6/mobile_ip6/exthdrs.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/exthdrs.c 2004-06-26 11:29:30.000000000 +0100 -@@ -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; -+} -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/exthdrs.h linux-2.4.25/net/ipv6/mobile_ip6/exthdrs.h ---- linux-2.4.25.old/net/ipv6/mobile_ip6/exthdrs.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/exthdrs.h 2004-06-26 11:29:30.000000000 +0100 -@@ -0,0 +1,47 @@ -+/* -+ * MIPL Mobile IPv6 Extension Headers header file -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#ifndef _MIPV6_EXTHDRS_H -+#define _MIPV6_EXTHDRS_H -+ -+struct in6_addr; -+struct sk_buff; -+struct ipv6_rt_hdr; -+struct ipv6_opt_hdr; -+struct ipv6_txoptions; -+struct flowi; -+struct dst_entry; -+/* -+ * Home Address Destination Option function prototypes -+ */ -+int mipv6_append_home_addr(__u8 *opt, int offset, struct in6_addr *addr); -+ -+int mipv6_handle_homeaddr(struct sk_buff *skb, int optoff); -+ -+void mipv6_icmp_swap_addrs(struct sk_buff *skb); -+ -+/* -+ * Creates a routing header of type 2. -+ */ -+void mipv6_append_rt2hdr(struct ipv6_rt_hdr *srcrt, struct in6_addr *addr); -+ -+/* Function to add the first destination option header, which may -+ * include a home address option. -+ */ -+void mipv6_append_dst1opts(struct ipv6_opt_hdr *dst1opt, struct in6_addr *saddr, -+ struct ipv6_opt_hdr *old_dst1opt, int len); -+ -+struct ipv6_txoptions *mipv6_modify_txoptions( -+ struct sock *sk, struct sk_buff *skb, -+ struct ipv6_txoptions *old_opt, struct flowi *fl, -+ struct dst_entry **dst); -+ -+#endif /* _MIPV6_EXTHDRS_H */ -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/ha.c linux-2.4.25/net/ipv6/mobile_ip6/ha.c ---- linux-2.4.25.old/net/ipv6/mobile_ip6/ha.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/ha.c 2004-06-26 11:29:30.000000000 +0100 -@@ -0,0 +1,553 @@ -+/* -+ * Home-agent functionality -+ * -+ * Authors: -+ * Sami Kivisaari <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; -+} -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/ha.h linux-2.4.25/net/ipv6/mobile_ip6/ha.h ---- linux-2.4.25.old/net/ipv6/mobile_ip6/ha.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/ha.h 2004-06-26 11:29:30.000000000 +0100 -@@ -0,0 +1,39 @@ -+/* -+ * MIPL Mobile IPv6 Home Agent header file -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#ifndef _HA_H -+#define _HA_H -+ -+int mipv6_ha_init(void); -+void mipv6_ha_exit(void); -+ -+int mipv6_dad_start(struct inet6_ifaddr *ifp, int ifindex, -+ struct in6_addr *daddr, struct in6_addr *haddr, -+ struct in6_addr *coa, struct in6_addr *rep_coa, -+ __u32 ba_lifetime, __u16 sequence, __u8 flags); -+ -+void mipv6_bu_finish(struct inet6_ifaddr *ifp, int ifindex, -+ __u8 ba_status, struct in6_addr *daddr, -+ struct in6_addr *haddr, struct in6_addr *coa, -+ struct in6_addr *rep_coa, __u32 ba_lifetime, -+ __u16 sequence, __u8 flags, __u8 *k_bu); -+ -+ -+static __inline__ void mipv6_generate_ll_addr(struct in6_addr *ll_addr, -+ struct in6_addr *addr) -+{ -+ ll_addr->s6_addr32[0] = htonl(0xfe800000); -+ ll_addr->s6_addr32[1] = 0; -+ ll_addr->s6_addr32[2] = addr->s6_addr32[2]; -+ ll_addr->s6_addr32[3] = addr->s6_addr32[3]; -+} -+ -+#endif -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/halist.c linux-2.4.25/net/ipv6/mobile_ip6/halist.c ---- linux-2.4.25.old/net/ipv6/mobile_ip6/halist.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/halist.c 2004-06-26 11:29:30.000000000 +0100 -@@ -0,0 +1,507 @@ -+/* -+ * Home Agents List -+ * -+ * Authors: -+ * Antti Tuominen <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); -+} -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/halist.h linux-2.4.25/net/ipv6/mobile_ip6/halist.h ---- linux-2.4.25.old/net/ipv6/mobile_ip6/halist.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/halist.h 2004-06-26 11:29:30.000000000 +0100 -@@ -0,0 +1,28 @@ -+/* -+ * MIPL Mobile IPv6 Home Agents List header file -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#ifndef _HALIST_H -+#define _HALIST_H -+ -+int mipv6_halist_init(__u32 size); -+ -+void mipv6_halist_exit(void); -+ -+int mipv6_halist_add(int ifindex, struct in6_addr *glob_addr, int plen, -+ struct in6_addr *ll_addr, unsigned int pref, __u32 lifetime); -+ -+int mipv6_halist_delete(struct in6_addr *glob_addr); -+ -+int mipv6_ha_get_pref_list(int ifindex, struct in6_addr **addrs, int max); -+ -+int mipv6_ha_get_addr(int ifindex, struct in6_addr *addr); -+ -+#endif /* _HALIST_H */ -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/hashlist.c linux-2.4.25/net/ipv6/mobile_ip6/hashlist.c ---- linux-2.4.25.old/net/ipv6/mobile_ip6/hashlist.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/hashlist.c 2004-06-26 11:29:31.000000000 +0100 -@@ -0,0 +1,351 @@ -+/* -+ * Generic hashtable with chaining. Supports secodary sort order -+ * with doubly linked-list. -+ * -+ * Authors: -+ * Sami Kivisaari <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); -+} -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/hashlist.h linux-2.4.25/net/ipv6/mobile_ip6/hashlist.h ---- linux-2.4.25.old/net/ipv6/mobile_ip6/hashlist.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/hashlist.h 2004-06-26 11:29:31.000000000 +0100 -@@ -0,0 +1,63 @@ -+/* -+ * MIPL Mobile IPv6 Hashlist header file -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#ifndef _HASHLIST_H -+#define _HASHLIST_H -+ -+#define ITERATOR_ERR -1 -+#define ITERATOR_CONT 0 -+#define ITERATOR_STOP 1 -+#define ITERATOR_DELETE_ENTRY 2 -+ -+struct kmem_cache_t; -+ -+struct hashlist_entry { -+ unsigned long sortkey; -+ struct list_head sorted; -+ struct list_head hashlist; -+}; -+ -+struct hashlist * hashlist_create( -+ int bucketnum, int max_entries, size_t size, char *name, -+ void (*ctor)(void *, kmem_cache_t *, unsigned long), -+ void (*dtor)(void *, kmem_cache_t *, unsigned long), -+ int (*compare)(void *data, void *hashkey), -+ __u32 (*hash_function)(void *hashkey)); -+ -+void hashlist_destroy(struct hashlist *hashlist); -+ -+void *hashlist_alloc(struct hashlist *hashlist, int type); -+ -+void hashlist_free(struct hashlist *hashlist, struct hashlist_entry *he); -+ -+struct hashlist_entry *hashlist_get(struct hashlist *hashlist, void *hashkey); -+ -+struct hashlist_entry *hashlist_get_ex( -+ struct hashlist *hashlist, void *hashkey, -+ int (*compare)(void *data, void *hashkey)); -+ -+int hashlist_add(struct hashlist *hashlist, void *hashkey, -+ unsigned long sortkey, void *data); -+ -+void hashlist_delete(struct hashlist *hashlist, struct hashlist_entry *he); -+ -+/* iterator function */ -+typedef int (*hashlist_iterator_t)(void *, void *, unsigned long *); -+ -+int hashlist_iterate(struct hashlist *hashlist, void *args, -+ hashlist_iterator_t func); -+ -+void * hashlist_get_first(struct hashlist *hashlist); -+ -+int hashlist_reposition(struct hashlist *hashlist, struct hashlist_entry *he, -+ unsigned long sortkey); -+ -+#endif -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/hmac.c linux-2.4.25/net/ipv6/mobile_ip6/hmac.c ---- linux-2.4.25.old/net/ipv6/mobile_ip6/hmac.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/hmac.c 2004-06-26 11:29:31.000000000 +0100 -@@ -0,0 +1,658 @@ -+/* Authentication algorithms -+ * -+ * Authors: -+ * Alexis Olivereau <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 */ -+} -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/hmac.h linux-2.4.25/net/ipv6/mobile_ip6/hmac.h ---- linux-2.4.25.old/net/ipv6/mobile_ip6/hmac.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/hmac.h 2004-06-26 11:29:31.000000000 +0100 -@@ -0,0 +1,94 @@ -+/* -+ * MIPL Mobile IPv6 Message authentication algorithms -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#ifndef _HMAC_H -+#define _HMAC_H -+ -+#include <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 */ -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/ioctl_mn.c linux-2.4.25/net/ipv6/mobile_ip6/ioctl_mn.c ---- linux-2.4.25.old/net/ipv6/mobile_ip6/ioctl_mn.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/ioctl_mn.c 2004-06-26 11:29:31.000000000 +0100 -@@ -0,0 +1,142 @@ -+/* -+ * Mobile Node IOCTL Control device -+ * -+ * Authors: -+ * Henrik Petander <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); -+} -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/mdetect.c linux-2.4.25/net/ipv6/mobile_ip6/mdetect.c ---- linux-2.4.25.old/net/ipv6/mobile_ip6/mdetect.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/mdetect.c 2004-06-26 11:29:31.000000000 +0100 -@@ -0,0 +1,1153 @@ -+/* -+ * Movement Detection Module -+ * -+ * Authors: -+ * Henrik Petander <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; -+} -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/mdetect.h linux-2.4.25/net/ipv6/mobile_ip6/mdetect.h ---- linux-2.4.25.old/net/ipv6/mobile_ip6/mdetect.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/mdetect.h 2004-06-26 11:29:31.000000000 +0100 -@@ -0,0 +1,37 @@ -+/* -+ * MIPL Mobile IPv6 Movement detection module header file -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#ifndef _MDETECT_H -+#define _MDETECT_H -+ -+struct handoff { -+ int home_address; /* Is the coa a home address */ -+ int ifindex; -+ int plen; -+ struct in6_addr *coa; -+ struct in6_addr rtr_addr; /* Prefix or rtr address if coa is home address */ -+}; -+ -+int mipv6_initialize_mdetect(void); -+ -+int mipv6_shutdown_mdetect(void); -+ -+int mipv6_get_care_of_address(struct in6_addr *homeaddr, struct in6_addr *coa); -+ -+int mipv6_mdet_del_if(int ifindex); -+ -+int mipv6_mdet_finalize_ho(const struct in6_addr *coa, const int ifindex); -+ -+void mipv6_mdet_retrigger_ho(void); -+ -+void mipv6_mdet_set_curr_rtr_reachable(int reachable); -+ -+#endif /* _MDETECT_H */ -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/mipv6_icmp.c linux-2.4.25/net/ipv6/mobile_ip6/mipv6_icmp.c ---- linux-2.4.25.old/net/ipv6/mobile_ip6/mipv6_icmp.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/mipv6_icmp.c 2004-06-26 11:29:31.000000000 +0100 -@@ -0,0 +1,342 @@ -+/** -+ * Generic icmp routines -+ * -+ * Authors: -+ * Jaakko Laine <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 */ -+} -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/mipv6_icmp.h linux-2.4.25/net/ipv6/mobile_ip6/mipv6_icmp.h ---- linux-2.4.25.old/net/ipv6/mobile_ip6/mipv6_icmp.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/mipv6_icmp.h 2004-06-26 11:29:31.000000000 +0100 -@@ -0,0 +1,43 @@ -+/* -+ * MIPL Mobile IPv6 ICMP send and receive prototypes -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#ifndef _MIPV6_ICMP -+#define _MIPV6_ICMP -+ -+#include <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 -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/mipv6_icmp_ha.c linux-2.4.25/net/ipv6/mobile_ip6/mipv6_icmp_ha.c ---- linux-2.4.25.old/net/ipv6/mobile_ip6/mipv6_icmp_ha.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/mipv6_icmp_ha.c 2004-06-26 11:29:31.000000000 +0100 -@@ -0,0 +1,158 @@ -+/* -+ * Home Agent specific ICMP routines -+ * -+ * Authors: -+ * Antti Tuominen <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 -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/mipv6_icmp_mn.c linux-2.4.25/net/ipv6/mobile_ip6/mipv6_icmp_mn.c ---- linux-2.4.25.old/net/ipv6/mobile_ip6/mipv6_icmp_mn.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/mipv6_icmp_mn.c 2004-06-26 11:29:31.000000000 +0100 -@@ -0,0 +1,273 @@ -+/* -+ * Mobile Node specific ICMP routines -+ * -+ * Authors: -+ * Antti Tuominen <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 -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/mn.c linux-2.4.25/net/ipv6/mobile_ip6/mn.c ---- linux-2.4.25.old/net/ipv6/mobile_ip6/mn.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/mn.c 2004-06-26 11:29:31.000000000 +0100 -@@ -0,0 +1,1521 @@ -+/* -+ * Mobile-node functionality -+ * -+ * Authors: -+ * Sami Kivisaari <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(); -+} -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/mn.h linux-2.4.25/net/ipv6/mobile_ip6/mn.h ---- linux-2.4.25.old/net/ipv6/mobile_ip6/mn.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/mn.h 2004-06-26 11:29:31.000000000 +0100 -@@ -0,0 +1,96 @@ -+/* -+ * MIPL Mobile IPv6 Mobile Node header file -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#ifndef _MN_H -+#define _MN_H -+ -+#include <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 */ -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/mobhdr.h linux-2.4.25/net/ipv6/mobile_ip6/mobhdr.h ---- linux-2.4.25.old/net/ipv6/mobile_ip6/mobhdr.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/mobhdr.h 2004-06-26 11:29:31.000000000 +0100 -@@ -0,0 +1,101 @@ -+/* -+ * MIPL Mobile IPv6 Mobility Header send and receive -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#ifndef _MOBHDR_H -+#define _MOBHDR_H -+ -+#include <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 */ -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/mobhdr_common.c linux-2.4.25/net/ipv6/mobile_ip6/mobhdr_common.c ---- linux-2.4.25.old/net/ipv6/mobile_ip6/mobhdr_common.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/mobhdr_common.c 2004-06-26 11:29:31.000000000 +0100 -@@ -0,0 +1,1210 @@ -+/* -+ * Mobile IPv6 Mobility Header Common Functions -+ * -+ * Authors: -+ * Antti Tuominen <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)); -+} -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/mobhdr_mn.c linux-2.4.25/net/ipv6/mobile_ip6/mobhdr_mn.c ---- linux-2.4.25.old/net/ipv6/mobile_ip6/mobhdr_mn.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/mobhdr_mn.c 2004-06-26 11:29:32.000000000 +0100 -@@ -0,0 +1,1155 @@ -+/* -+ * Mobile IPv6 Mobility Header Functions for Mobile Node -+ * -+ * Authors: -+ * Antti Tuominen <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); -+} -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/module_cn.c linux-2.4.25/net/ipv6/mobile_ip6/module_cn.c ---- linux-2.4.25.old/net/ipv6/mobile_ip6/module_cn.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/module_cn.c 2004-06-26 11:29:31.000000000 +0100 -@@ -0,0 +1,167 @@ -+/* -+ * Mobile IPv6 Common Module -+ * -+ * Authors: -+ * Sami Kivisaari <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 */ -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/module_ha.c linux-2.4.25/net/ipv6/mobile_ip6/module_ha.c ---- linux-2.4.25.old/net/ipv6/mobile_ip6/module_ha.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/module_ha.c 2004-06-26 11:29:31.000000000 +0100 -@@ -0,0 +1,264 @@ -+/* -+ * Mobile IPv6 Home Agent Module -+ * -+ * Authors: -+ * Sami Kivisaari <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 */ -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/module_mn.c linux-2.4.25/net/ipv6/mobile_ip6/module_mn.c ---- linux-2.4.25.old/net/ipv6/mobile_ip6/module_mn.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/module_mn.c 2004-06-26 11:29:31.000000000 +0100 -@@ -0,0 +1,188 @@ -+/* -+ * Mobile IPv6 Mobile Node Module -+ * -+ * Authors: -+ * Sami Kivisaari <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 */ -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/multiaccess_ctl.c linux-2.4.25/net/ipv6/mobile_ip6/multiaccess_ctl.c ---- linux-2.4.25.old/net/ipv6/mobile_ip6/multiaccess_ctl.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/multiaccess_ctl.c 2004-06-26 11:29:31.000000000 +0100 -@@ -0,0 +1,287 @@ -+/* -+ * 2001 (c) Oy L M Ericsson Ab -+ * -+ * Author: NomadicLab / Ericsson Research <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"); -+} -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/multiaccess_ctl.h linux-2.4.25/net/ipv6/mobile_ip6/multiaccess_ctl.h ---- linux-2.4.25.old/net/ipv6/mobile_ip6/multiaccess_ctl.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/multiaccess_ctl.h 2004-06-26 11:29:31.000000000 +0100 -@@ -0,0 +1,77 @@ -+/* -+ * 2001 (c) Oy L M Ericsson Ab -+ * -+ * Author: NomadicLab / Ericsson Research <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 -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/ndisc_ha.c linux-2.4.25/net/ipv6/mobile_ip6/ndisc_ha.c ---- linux-2.4.25.old/net/ipv6/mobile_ip6/ndisc_ha.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/ndisc_ha.c 2004-06-26 11:29:31.000000000 +0100 -@@ -0,0 +1,596 @@ -+/* -+ * Mobile IPv6 Duplicate Address Detection Functions -+ * -+ * Authors: -+ * Krishna Kumar <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); -+} -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/prefix.c linux-2.4.25/net/ipv6/mobile_ip6/prefix.c ---- linux-2.4.25.old/net/ipv6/mobile_ip6/prefix.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/prefix.c 2004-06-26 11:29:31.000000000 +0100 -@@ -0,0 +1,217 @@ -+/** -+ * Prefix solicitation and advertisement -+ * -+ * Authors: -+ * Jaakko Laine <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); -+} -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/prefix.h linux-2.4.25/net/ipv6/mobile_ip6/prefix.h ---- linux-2.4.25.old/net/ipv6/mobile_ip6/prefix.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/prefix.h 2004-06-26 11:29:31.000000000 +0100 -@@ -0,0 +1,57 @@ -+/* -+ * MIPL Mobile IPv6 Prefix solicitation and advertisement -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#ifndef _PREFIX_H -+#define _PREFIX_H -+ -+#include <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 -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/prefix_ha.c linux-2.4.25/net/ipv6/mobile_ip6/prefix_ha.c ---- linux-2.4.25.old/net/ipv6/mobile_ip6/prefix_ha.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/prefix_ha.c 2004-06-26 11:29:31.000000000 +0100 -@@ -0,0 +1,122 @@ -+/** -+ * Prefix advertisement for Home Agent -+ * -+ * Authors: -+ * Jaakko Laine <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); -+} -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/rr_crypto.c linux-2.4.25/net/ipv6/mobile_ip6/rr_crypto.c ---- linux-2.4.25.old/net/ipv6/mobile_ip6/rr_crypto.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/rr_crypto.c 2004-06-26 11:29:31.000000000 +0100 -@@ -0,0 +1,255 @@ -+/* -+ * rr_cookie.c - Mobile IPv6 return routability crypto -+ * Author : Henrik Petander <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 -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/rr_crypto.h linux-2.4.25/net/ipv6/mobile_ip6/rr_crypto.h ---- linux-2.4.25.old/net/ipv6/mobile_ip6/rr_crypto.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/rr_crypto.h 2004-06-26 11:29:31.000000000 +0100 -@@ -0,0 +1,72 @@ -+/* -+ * MIPL Mobile IPv6 Return routability crypto prototypes -+ * -+ * $Id:$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#ifndef _RR_CRYPTO -+#define _RR_CRYPTO -+ -+#include <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 */ -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/sortedlist.c linux-2.4.25/net/ipv6/mobile_ip6/sortedlist.c ---- linux-2.4.25.old/net/ipv6/mobile_ip6/sortedlist.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/sortedlist.c 2004-06-26 11:29:31.000000000 +0100 -@@ -0,0 +1,349 @@ -+/** -+ * Sorted list - linked list with sortkey. -+ * -+ * Authors: -+ * Jaakko Laine <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; -+} -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/sortedlist.h linux-2.4.25/net/ipv6/mobile_ip6/sortedlist.h ---- linux-2.4.25.old/net/ipv6/mobile_ip6/sortedlist.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/sortedlist.h 2004-06-26 11:29:31.000000000 +0100 -@@ -0,0 +1,133 @@ -+/* -+ * Sorted list - linked list with sortkey -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+/** -+ * compare - compares two arbitrary data items -+ * @data1: first data item -+ * @data2: second data item -+ * @datalen: length of data items in bits -+ * -+ * datalen is in bits! -+ */ -+int mipv6_bitwise_compare(const void *data1, const void *data2, int datalen); -+ -+/** -+ * mipv6_slist_add - add an entry to sorted list -+ * @head: list_head of the sorted list -+ * @data: item to store -+ * @datalen: length of data (in bytes) -+ * @key: sortkey of item -+ * -+ * Allocates memory for entry and data -+ */ -+int mipv6_slist_add(struct list_head *head, void *data, int datalen, -+ unsigned long sortkey); -+ -+/** -+ * mipv6_slist_get_first - get the first data item in the list -+ * @head: list_head of the sorted list -+ * -+ * Returns the actual data item, not copy, so don't kfree it -+ */ -+void *mipv6_slist_get_first(struct list_head *head); -+ -+/** -+ * mipv6_slist_del_first - delete (and get) the first item in list -+ * @head: list_head of the sorted list -+ * -+ * Remember to kfree the item -+ */ -+void *mipv6_slist_del_first(struct list_head *head); -+ -+/** -+ * mipv6_slist_del_item - delete entry -+ * @head: list_head of the sorted list -+ * @data: item to delete -+ * @compare: function used for comparing the data items -+ * -+ * compare function needs to have prototype -+ * int (*compare)(const void *data1, const void *data2, int datalen) where -+ * datalen is in bits -+ */ -+int mipv6_slist_del_item(struct list_head *head, void *data, -+ int (*compare)(const void *data1, const void *data2, -+ int datalen)); -+ -+/** -+ * mipv6_slist_get_first_key - get sortkey of the first item -+ * @head: list_head of the sorted list -+ */ -+unsigned long mipv6_slist_get_first_key(struct list_head *head); -+ -+/** -+ * mipv6_slist_get_key - get sortkey of the data item -+ * @head: list_head of the sorted list -+ * @data: the item to search for -+ * @compare: function used for comparing the data items -+ * -+ * compare function needs to have prototype -+ * int (*compare)(const void *data1, const void *data2, int datalen) where -+ * datalen is in bits -+ */ -+unsigned long mipv6_slist_get_key(struct list_head *head, void *data, -+ int (*compare)(const void *data1, -+ const void *data2, -+ int datalen)); -+ -+/** -+ * mipv6_slist_get_data - get the data item identified by sortkey -+ * @head: list_head of the sorted list -+ * @key: sortkey of the item -+ * -+ * Returns the actual data item, not copy, so don't kfree it -+ */ -+void *mipv6_slist_get_data(struct list_head *head, unsigned long sortkey); -+ -+/** -+ * mipv6_slist_modify - modify data item -+ * @head: list_head of the sorted list -+ * @data: item, whose sortkey is to be modified -+ * @datalen: datalen in bytes -+ * @new_key: new sortkey -+ * @compare: function used for comparing the data items -+ * -+ * Compies the new data on top of the old one, if compare function returns -+ * non-negative. If there's no matching entry, new one will be created. -+ * Compare function needs to have prototype -+ * int (*compare)(const void *data1, const void *data2, int datalen) where -+ * datalen is in bits. -+ */ -+int mipv6_slist_modify(struct list_head *head, void *data, int datalen, -+ unsigned long new_key, -+ int (*compare)(const void *data1, const void *data2, -+ int datalen)); -+ -+/** -+ * mipv6_slist_push_first - move the first entry to place indicated by new_key -+ * @head: list_head of the sorted list -+ * @new_key: new sortkey -+ */ -+int mipv6_slist_push_first(struct list_head *head, unsigned long new_key); -+ -+/** -+ * mipv6_slist_for_each - apply func to every item in list -+ * @head: list_head of the sorted list -+ * @args: args to pass to func -+ * @func: function to use -+ * -+ * function must be of type -+ * int (*func)(void *data, void *args, unsigned long sortkey) -+ * List iteration will stop once func has been applied to every item -+ * or when func returns true -+ */ -+int mipv6_slist_for_each(struct list_head *head, void *args, -+ int (*func)(void *data, void *args, -+ unsigned long sortkey)); -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/stats.c linux-2.4.25/net/ipv6/mobile_ip6/stats.c ---- linux-2.4.25.old/net/ipv6/mobile_ip6/stats.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/stats.c 2004-06-26 11:29:32.000000000 +0100 -@@ -0,0 +1,90 @@ -+/* -+ * Statistics module -+ * -+ * Authors: -+ * Sami Kivisaari <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"); -+} -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/stats.h linux-2.4.25/net/ipv6/mobile_ip6/stats.h ---- linux-2.4.25.old/net/ipv6/mobile_ip6/stats.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/stats.h 2004-06-26 11:29:32.000000000 +0100 -@@ -0,0 +1,71 @@ -+/* -+ * MIPL Mobile IPv6 Statistics header file -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#ifndef _STATS_H -+#define _STATS_H -+ -+struct mipv6_drop { -+ __u32 auth; -+ __u32 invalid; -+ __u32 misc; -+}; -+ -+struct mipv6_statistics { -+ int n_encapsulations; -+ int n_decapsulations; -+ int n_mh_in_msg; -+ int n_mh_in_error; -+ int n_mh_out_msg; -+ int n_mh_out_error; -+ -+ int n_brr_rcvd; -+ int n_hoti_rcvd; -+ int n_coti_rcvd; -+ int n_hot_rcvd; -+ int n_cot_rcvd; -+ int n_bu_rcvd; -+ int n_ba_rcvd; -+ int n_ban_rcvd; -+ int n_be_rcvd; -+ -+ int n_brr_sent; -+ int n_hoti_sent; -+ int n_coti_sent; -+ int n_hot_sent; -+ int n_cot_sent; -+ int n_bu_sent; -+ int n_ba_sent; -+ int n_ban_sent; -+ int n_be_sent; -+ -+ int n_ha_rcvd; -+ int n_ha_sent; -+ -+ struct mipv6_drop n_bu_drop; -+ struct mipv6_drop n_ba_drop; -+ struct mipv6_drop n_brr_drop; -+ struct mipv6_drop n_be_drop; -+ struct mipv6_drop n_ha_drop; -+}; -+ -+extern struct mipv6_statistics mipv6_stats; -+ -+#ifdef CONFIG_SMP -+/* atomic_t is max 24 bits long */ -+#define MIPV6_INC_STATS(X) atomic_inc((atomic_t *)&mipv6_stats.X); -+#else -+#define MIPV6_INC_STATS(X) mipv6_stats.X++; -+#endif -+ -+int mipv6_stats_init(void); -+void mipv6_stats_exit(void); -+ -+#endif -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/tunnel.h linux-2.4.25/net/ipv6/mobile_ip6/tunnel.h ---- linux-2.4.25.old/net/ipv6/mobile_ip6/tunnel.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/tunnel.h 2004-06-26 11:29:32.000000000 +0100 -@@ -0,0 +1,35 @@ -+/* -+ * MIPL Mobile IPv6 IP6-IP6 tunneling header file -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#ifndef _TUNNEL_H -+#define _TUNNEL_H -+ -+#include <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 -+ -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/tunnel_ha.c linux-2.4.25/net/ipv6/mobile_ip6/tunnel_ha.c ---- linux-2.4.25.old/net/ipv6/mobile_ip6/tunnel_ha.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/tunnel_ha.c 2004-06-26 11:29:32.000000000 +0100 -@@ -0,0 +1,264 @@ -+/* -+ * IPv6-IPv6 tunneling module -+ * -+ * Authors: -+ * Sami Kivisaari <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); -+} -+ -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/tunnel_ha.h linux-2.4.25/net/ipv6/mobile_ip6/tunnel_ha.h ---- linux-2.4.25.old/net/ipv6/mobile_ip6/tunnel_ha.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/tunnel_ha.h 2004-06-26 11:29:32.000000000 +0100 -@@ -0,0 +1,20 @@ -+#ifndef _TUNNEL_HA_H -+#define _TUNNEL_HA_H -+ -+#include "tunnel.h" -+ -+extern int mipv6_max_tnls; -+extern int mipv6_min_tnls; -+ -+extern void mipv6_initialize_tunnel(void); -+extern void mipv6_shutdown_tunnel(void); -+ -+extern int mipv6_add_tnl_to_mn(struct in6_addr *coa, -+ struct in6_addr *ha_addr, -+ struct in6_addr *home_addr); -+ -+extern int mipv6_del_tnl_to_mn(struct in6_addr *coa, -+ struct in6_addr *ha_addr, -+ struct in6_addr *home_addr); -+ -+#endif -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/tunnel_mn.c linux-2.4.25/net/ipv6/mobile_ip6/tunnel_mn.c ---- linux-2.4.25.old/net/ipv6/mobile_ip6/tunnel_mn.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/tunnel_mn.c 2004-06-26 11:29:32.000000000 +0100 -@@ -0,0 +1,160 @@ -+/* -+ * IPv6-IPv6 tunneling module -+ * -+ * Authors: -+ * Sami Kivisaari <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(); -+} -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/tunnel_mn.h linux-2.4.25/net/ipv6/mobile_ip6/tunnel_mn.h ---- linux-2.4.25.old/net/ipv6/mobile_ip6/tunnel_mn.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/tunnel_mn.h 2004-06-26 11:29:32.000000000 +0100 -@@ -0,0 +1,14 @@ -+#ifndef _TUNNEL_MN_H -+#define _TUNNEL_MN_H -+ -+#include "tunnel.h" -+ -+extern int mipv6_add_tnl_to_ha(void); -+ -+extern int mipv6_mv_tnl_to_ha(struct in6_addr *ha_addr, -+ struct in6_addr *coa, -+ struct in6_addr *home_addr); -+ -+extern int mipv6_del_tnl_to_ha(void); -+ -+#endif -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/util.h linux-2.4.25/net/ipv6/mobile_ip6/util.h ---- linux-2.4.25.old/net/ipv6/mobile_ip6/util.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/util.h 2004-06-26 11:29:32.000000000 +0100 -@@ -0,0 +1,91 @@ -+/* -+ * MIPL Mobile IPv6 Utility functions -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#ifndef _UTIL_H -+#define _UTIL_H -+ -+#include <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 */ -diff -uprN linux-2.4.25.old/net/ipv6/ndisc.c linux-2.4.25/net/ipv6/ndisc.c ---- linux-2.4.25.old/net/ipv6/ndisc.c 2003-11-28 18:26:21.000000000 +0000 -+++ linux-2.4.25/net/ipv6/ndisc.c 2004-06-26 11:29:32.000000000 +0100 -@@ -23,6 +23,7 @@ - * and moved to net/core. - * Pekka Savola : RFC2461 validation - * YOSHIFUJI Hideaki @USAGI : Verify ND options properly -+ * Ville Nuorvala : RFC2461 fixes to proxy ND - */ - - /* Set to 3 to get tracing... */ -@@ -70,6 +71,7 @@ - #include <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 @@ struct ndisc_options *ndisc_parse_option - case ND_OPT_TARGET_LL_ADDR: - case ND_OPT_MTU: - case ND_OPT_REDIRECT_HDR: -+ case ND_OPT_RTR_ADV_INTERVAL: -+ case ND_OPT_HOME_AGENT_INFO: - if (ndopts->nd_opt_array[nd_opt->nd_opt_type]) { - ND_PRINTK2((KERN_WARNING - "ndisc_parse_options(): duplicated ND6 option found: type=%d\n", -@@ -372,8 +376,8 @@ ndisc_build_ll_hdr(struct sk_buff *skb, - */ - - void ndisc_send_na(struct net_device *dev, struct neighbour *neigh, -- struct in6_addr *daddr, struct in6_addr *solicited_addr, -- int router, int solicited, int override, int inc_opt) -+ struct in6_addr *daddr, struct in6_addr *solicited_addr, -+ int router, int solicited, int override, int inc_opt) - { - static struct in6_addr tmpaddr; - struct inet6_ifaddr *ifp; -@@ -766,7 +770,8 @@ void ndisc_recv_ns(struct sk_buff *skb) - int addr_type = ipv6_addr_type(saddr); - - if (in6_dev && in6_dev->cnf.forwarding && -- (addr_type & IPV6_ADDR_UNICAST) && -+ (addr_type & IPV6_ADDR_UNICAST || -+ addr_type == IPV6_ADDR_ANY) && - pneigh_lookup(&nd_tbl, &msg->target, dev, 0)) { - int inc = ipv6_addr_type(daddr)&IPV6_ADDR_MULTICAST; - -@@ -778,13 +783,21 @@ void ndisc_recv_ns(struct sk_buff *skb) - nd_tbl.stats.rcv_probes_mcast++; - else - nd_tbl.stats.rcv_probes_ucast++; -- -- neigh = neigh_event_ns(&nd_tbl, lladdr, saddr, dev); - -- if (neigh) { -- ndisc_send_na(dev, neigh, saddr, &msg->target, -- 0, 1, 0, 1); -- neigh_release(neigh); -+ if (addr_type & IPV6_ADDR_UNICAST) { -+ neigh = neigh_event_ns(&nd_tbl, lladdr, saddr, dev); -+ -+ if (neigh) { -+ ndisc_send_na(dev, neigh, saddr, &msg->target, -+ 0, 1, 0, 1); -+ neigh_release(neigh); -+ } -+ } else { -+ /* the proxy should also protect against DAD */ -+ struct in6_addr maddr; -+ ipv6_addr_all_nodes(&maddr); -+ ndisc_send_na(dev, NULL, &maddr, &msg->target, -+ 0, 0, 0, 1); - } - } else { - struct sk_buff *n = skb_clone(skb, GFP_ATOMIC); -@@ -849,6 +862,9 @@ void ndisc_recv_na(struct sk_buff *skb) - if (ifp->flags & IFA_F_TENTATIVE) { - addrconf_dad_failure(ifp); - return; -+ } else if (ndisc_mip_mn_ha_probe(ifp, lladdr)) { -+ in6_ifa_put(ifp); -+ return; - } - /* What should we make now? The advertisement - is invalid, but ndisc specs say nothing -@@ -887,6 +903,7 @@ void ndisc_recv_na(struct sk_buff *skb) - msg->icmph.icmp6_override, 1); - neigh_release(neigh); - } -+ ndisc_check_mipv6_dad(&msg->target); - } - - static void ndisc_router_discovery(struct sk_buff *skb) -@@ -894,6 +911,7 @@ static void ndisc_router_discovery(struc - struct ra_msg *ra_msg = (struct ra_msg *) skb->h.raw; - struct neighbour *neigh; - struct inet6_dev *in6_dev; -+ int change_rtr; - struct rt6_info *rt; - int lifetime; - struct ndisc_options ndopts; -@@ -923,10 +941,6 @@ static void ndisc_router_discovery(struc - ND_PRINTK1("RA: can't find in6 device\n"); - return; - } -- if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_ra) { -- in6_dev_put(in6_dev); -- return; -- } - - if (!ndisc_parse_options(opt, optlen, &ndopts)) { - in6_dev_put(in6_dev); -@@ -935,7 +949,12 @@ static void ndisc_router_discovery(struc - "ICMP6 RA: invalid ND option, ignored.\n"); - return; - } -+ change_rtr = ndisc_mipv6_ra_rcv(skb, &ndopts); - -+ if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_ra) { -+ in6_dev_put(in6_dev); -+ return; -+ } - if (in6_dev->if_flags & IF_RS_SENT) { - /* - * flag that an RA was received after an RS was sent -@@ -963,8 +982,7 @@ static void ndisc_router_discovery(struc - ip6_del_rt(rt, NULL); - rt = NULL; - } -- -- if (rt == NULL && lifetime) { -+ if (rt == NULL && lifetime && change_rtr) { - ND_PRINTK2("ndisc_rdisc: adding default router\n"); - - rt = rt6_add_dflt_router(&skb->nh.ipv6h->saddr, skb->dev); -@@ -1087,6 +1105,8 @@ out: - if (rt) - dst_release(&rt->u.dst); - in6_dev_put(in6_dev); -+ -+ ndisc_mipv6_change_router(change_rtr); - } - - static void ndisc_redirect_rcv(struct sk_buff *skb) -diff -uprN linux-2.4.25.old/net/ipv6/raw.c linux-2.4.25/net/ipv6/raw.c ---- linux-2.4.25.old/net/ipv6/raw.c 2003-11-28 18:26:21.000000000 +0000 -+++ linux-2.4.25/net/ipv6/raw.c 2004-06-26 11:29:32.000000000 +0100 -@@ -43,6 +43,7 @@ - #include <net/transp_v6.h> - #include <net/udp.h> - #include <net/inet_common.h> -+#include <net/mipglue.h> - - #include <net/rawv6.h> - -@@ -636,6 +637,7 @@ static int rawv6_sendmsg(struct sock *sk - hdr.daddr = daddr; - else - hdr.daddr = NULL; -+ hdr.daddr = mipv6_get_fake_hdr_daddr(hdr.daddr, daddr); - - err = ip6_build_xmit(sk, rawv6_frag_cksum, &hdr, &fl, len, - opt, hlimit, msg->msg_flags); -diff -uprN linux-2.4.25.old/net/ipv6/route.c linux-2.4.25/net/ipv6/route.c ---- linux-2.4.25.old/net/ipv6/route.c 2004-02-18 13:36:32.000000000 +0000 -+++ linux-2.4.25/net/ipv6/route.c 2004-06-26 11:29:32.000000000 +0100 -@@ -49,6 +49,7 @@ - #include <net/addrconf.h> - #include <net/tcp.h> - #include <linux/rtnetlink.h> -+#include <net/mipglue.h> - - #include <asm/uaccess.h> - -@@ -363,12 +364,8 @@ static struct rt6_info *rt6_cow(struct r - rt->u.dst.flags |= DST_HOST; - - #ifdef CONFIG_IPV6_SUBTREES -- if (rt->rt6i_src.plen && saddr) { -- ipv6_addr_copy(&rt->rt6i_src.addr, saddr); -- rt->rt6i_src.plen = 128; -- } -+ rt->rt6i_src.plen = ort->rt6i_src.plen; - #endif -- - rt->rt6i_nexthop = ndisc_get_neigh(rt->rt6i_dev, &rt->rt6i_gateway); - - dst_hold(&rt->u.dst); -@@ -511,14 +508,19 @@ struct dst_entry * ip6_route_output(stru - struct rt6_info *rt; - int strict; - int attempts = 3; -+ struct in6_addr *saddr; - -+ if (ipv6_chk_addr(fl->nl_u.ip6_u.daddr, NULL)) -+ saddr = NULL; -+ else -+ saddr = fl->nl_u.ip6_u.saddr; -+ - strict = ipv6_addr_type(fl->nl_u.ip6_u.daddr) & (IPV6_ADDR_MULTICAST|IPV6_ADDR_LINKLOCAL); - - relookup: - read_lock_bh(&rt6_lock); - -- fn = fib6_lookup(&ip6_routing_table, fl->nl_u.ip6_u.daddr, -- fl->nl_u.ip6_u.saddr); -+ fn = fib6_lookup(&ip6_routing_table, fl->nl_u.ip6_u.daddr, saddr); - - restart: - rt = fn->leaf; -@@ -663,25 +665,6 @@ out: - return (atomic_read(&ip6_dst_ops.entries) > ip6_rt_max_size); - } - --/* Clean host part of a prefix. Not necessary in radix tree, -- but results in cleaner routing tables. -- -- Remove it only when all the things will work! -- */ -- --static void ipv6_addr_prefix(struct in6_addr *pfx, -- const struct in6_addr *addr, int plen) --{ -- int b = plen&0x7; -- int o = plen>>3; -- -- memcpy(pfx->s6_addr, addr, o); -- if (o < 16) -- memset(pfx->s6_addr + o, 0, 16 - o); -- if (b != 0) -- pfx->s6_addr[o] = addr->s6_addr[o]&(0xff00 >> b); --} -- - static int ipv6_get_mtu(struct net_device *dev) - { - int mtu = IPV6_MIN_MTU; -@@ -810,7 +793,7 @@ int ip6_route_add(struct in6_rtmsg *rtms - if (!(gwa_type&IPV6_ADDR_UNICAST)) - goto out; - -- grt = rt6_lookup(gw_addr, NULL, rtmsg->rtmsg_ifindex, 1); -+ grt = rt6_lookup(gw_addr, &rtmsg->rtmsg_src, rtmsg->rtmsg_ifindex, 1); - - err = -EHOSTUNREACH; - if (grt == NULL) -@@ -848,7 +831,15 @@ int ip6_route_add(struct in6_rtmsg *rtms - goto out; - } - } -- -+#ifdef USE_IPV6_MOBILITY -+ /* If destination is mobile node, add special skb->dst->input -+ * function for proxy ND. -+ */ -+ if (rtmsg->rtmsg_flags & RTF_MOBILENODE) { -+ rt->u.dst.input = ip6_mipv6_forward; -+ } -+#endif /* CONFIG_IPV6_MOBILITY */ -+ - if (ipv6_addr_is_multicast(&rt->rt6i_dst.addr)) - rt->rt6i_hoplimit = IPV6_DEFAULT_MCASTHOPS; - else -@@ -936,7 +927,7 @@ void rt6_redirect(struct in6_addr *dest, - struct rt6_info *rt, *nrt; - - /* Locate old route to this destination. */ -- rt = rt6_lookup(dest, NULL, neigh->dev->ifindex, 1); -+ rt = rt6_lookup(dest, saddr, neigh->dev->ifindex, 1); - - if (rt == NULL) - return; -@@ -1003,6 +994,9 @@ source_ok: - nrt = ip6_rt_copy(rt); - if (nrt == NULL) - goto out; -+#ifdef CONFIG_IPV6_SUBTREES -+ nrt->rt6i_src.plen = rt->rt6i_src.plen; -+#endif - - nrt->rt6i_flags = RTF_GATEWAY|RTF_UP|RTF_DYNAMIC|RTF_CACHE; - if (on_link) -@@ -1104,6 +1098,9 @@ void rt6_pmtu_discovery(struct in6_addr - nrt = ip6_rt_copy(rt); - if (nrt == NULL) - goto out; -+#ifdef CONFIG_IPV6_SUBTREES -+ nrt->rt6i_src.plen = rt->rt6i_src.plen; -+#endif - ipv6_addr_copy(&nrt->rt6i_dst.addr, daddr); - nrt->rt6i_dst.plen = 128; - nrt->u.dst.flags |= DST_HOST; -diff -uprN linux-2.4.25.old/net/ipv6/tcp_ipv6.c linux-2.4.25/net/ipv6/tcp_ipv6.c ---- linux-2.4.25.old/net/ipv6/tcp_ipv6.c 2003-11-28 18:26:21.000000000 +0000 -+++ linux-2.4.25/net/ipv6/tcp_ipv6.c 2004-06-26 11:29:32.000000000 +0100 -@@ -50,6 +50,7 @@ - #include <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 @@ static int tcp_v6_connect(struct sock *s - struct flowi fl; - struct dst_entry *dst; - int addr_type; -+ int reroute = 0; - int err; - - if (addr_len < SIN6_LEN_RFC2133) -@@ -660,7 +662,7 @@ static int tcp_v6_connect(struct sock *s - - fl.proto = IPPROTO_TCP; - fl.fl6_dst = &np->daddr; -- fl.fl6_src = saddr; -+ fl.fl6_src = saddr; - fl.oif = sk->bound_dev_if; - fl.uli_u.ports.dport = usin->sin6_port; - fl.uli_u.ports.sport = sk->sport; -@@ -669,31 +671,46 @@ static int tcp_v6_connect(struct sock *s - struct rt0_hdr *rt0 = (struct rt0_hdr *) np->opt->srcrt; - fl.nl_u.ip6_u.daddr = rt0->addr; - } -- - dst = ip6_route_output(sk, &fl); -- -+#ifdef CONFIG_IPV6_SUBTREES -+ reroute = (saddr == NULL); -+#endif - if ((err = dst->error) != 0) { - dst_release(dst); - goto failure; - } -- -- ip6_dst_store(sk, dst, NULL); -- sk->route_caps = dst->dev->features&~NETIF_F_IP_CSUM; -- -+ if (!reroute) { -+ ip6_dst_store(sk, dst, NULL, NULL); -+ sk->route_caps = dst->dev->features&~NETIF_F_IP_CSUM; -+ } - if (saddr == NULL) { - err = ipv6_get_saddr(dst, &np->daddr, &saddr_buf); -+ -+ if (reroute) -+ dst_release(dst); - if (err) - goto failure; - - saddr = &saddr_buf; -+ ipv6_addr_copy(&np->rcv_saddr, saddr); -+#ifdef CONFIG_IPV6_SUBTREES -+ fl.fl6_src = saddr; -+ dst = ip6_route_output(sk, &fl); -+ -+ if ((err = dst->error) != 0) { -+ dst_release(dst); -+ goto failure; -+ } -+ ip6_dst_store(sk, dst, NULL, NULL); -+ sk->route_caps = dst->dev->features&~NETIF_F_IP_CSUM; -+#endif - } - - /* set the source address */ -- ipv6_addr_copy(&np->rcv_saddr, saddr); - ipv6_addr_copy(&np->saddr, saddr); - sk->rcv_saddr= LOOPBACK4_IPV6; - -- tp->ext_header_len = 0; -+ tp->ext_header_len = tcp_v6_get_mipv6_header_len(); - if (np->opt) - tp->ext_header_len = np->opt->opt_flen+np->opt->opt_nflen; - tp->mss_clamp = IPV6_MIN_MTU - sizeof(struct tcphdr) - sizeof(struct ipv6hdr); -@@ -1338,7 +1355,7 @@ static struct sock * tcp_v6_syn_recv_soc - #endif - MOD_INC_USE_COUNT; - -- ip6_dst_store(newsk, dst, NULL); -+ ip6_dst_store(newsk, dst, NULL, NULL); - sk->route_caps = dst->dev->features&~NETIF_F_IP_CSUM; - - newtp = &(newsk->tp_pinfo.af_tcp); -@@ -1383,7 +1400,7 @@ static struct sock * tcp_v6_syn_recv_soc - sock_kfree_s(sk, opt, opt->tot_len); - } - -- newtp->ext_header_len = 0; -+ newtp->ext_header_len = tcp_v6_get_mipv6_header_len(); - if (np->opt) - newtp->ext_header_len = np->opt->opt_nflen + np->opt->opt_flen; - -@@ -1710,7 +1727,7 @@ static int tcp_v6_rebuild_header(struct - return err; - } - -- ip6_dst_store(sk, dst, NULL); -+ ip6_dst_store(sk, dst, NULL, NULL); - sk->route_caps = dst->dev->features&~NETIF_F_IP_CSUM; - } - -@@ -1749,7 +1766,7 @@ static int tcp_v6_xmit(struct sk_buff *s - return -sk->err_soft; - } - -- ip6_dst_store(sk, dst, NULL); -+ ip6_dst_store(sk, dst, NULL, NULL); - } - - skb->dst = dst_clone(dst); -diff -uprN linux-2.4.25.old/net/ipv6/udp.c linux-2.4.25/net/ipv6/udp.c ---- linux-2.4.25.old/net/ipv6/udp.c 2004-02-18 13:36:32.000000000 +0000 -+++ linux-2.4.25/net/ipv6/udp.c 2004-06-26 11:29:32.000000000 +0100 -@@ -48,6 +48,7 @@ - #include <net/ip.h> - #include <net/udp.h> - #include <net/inet_common.h> -+#include <net/mipglue.h> - - #include <net/checksum.h> - -@@ -232,6 +233,7 @@ int udpv6_connect(struct sock *sk, struc - struct ip6_flowlabel *flowlabel = NULL; - int addr_type; - int err; -+ int reroute = 0; - - if (usin->sin6_family == AF_INET) { - if (__ipv6_only_sock(sk)) -@@ -331,7 +333,7 @@ ipv4_connected: - - fl.proto = IPPROTO_UDP; - fl.fl6_dst = &np->daddr; -- fl.fl6_src = &saddr; -+ fl.fl6_src = NULL; - fl.oif = sk->bound_dev_if; - fl.uli_u.ports.dport = sk->dport; - fl.uli_u.ports.sport = sk->sport; -@@ -348,29 +350,44 @@ ipv4_connected: - struct rt0_hdr *rt0 = (struct rt0_hdr *) np->opt->srcrt; - fl.fl6_dst = rt0->addr; - } -- - dst = ip6_route_output(sk, &fl); -- - if ((err = dst->error) != 0) { - dst_release(dst); - fl6_sock_release(flowlabel); -- return err; -- } -- -- ip6_dst_store(sk, dst, fl.fl6_dst); -- -+ return err; -+ } -+#ifdef CONFIG_IPV6_SUBTREES -+ reroute = (fl.fl6_src == NULL); -+#endif - /* get the source adddress used in the apropriate device */ - - err = ipv6_get_saddr(dst, daddr, &saddr); - -+ if (reroute) -+ dst_release(dst); -+ - if (err == 0) { -- if(ipv6_addr_any(&np->saddr)) -+#ifdef CONFIG_IPV6_SUBTREES -+ if (reroute) { -+ fl.fl6_src = &saddr; -+ dst = ip6_route_output(sk, &fl); -+ if ((err = dst->error) != 0) { -+ dst_release(dst); -+ fl6_sock_release(flowlabel); -+ return err; -+ } -+ } -+#endif -+ if(ipv6_addr_any(&np->saddr)) { - ipv6_addr_copy(&np->saddr, &saddr); -- -+ fl.fl6_src = &np->saddr; -+ } - if(ipv6_addr_any(&np->rcv_saddr)) { - ipv6_addr_copy(&np->rcv_saddr, &saddr); - sk->rcv_saddr = LOOPBACK4_IPV6; - } -+ ip6_dst_store(sk, dst, fl.fl6_dst, -+ fl.fl6_src == &np->saddr ? fl.fl6_src : NULL); - sk->state = TCP_ESTABLISHED; - } - fl6_sock_release(flowlabel); -@@ -894,6 +911,7 @@ static int udpv6_sendmsg(struct sock *sk - opt = fl6_merge_options(&opt_space, flowlabel, opt); - if (opt && opt->srcrt) - udh.daddr = daddr; -+ udh.daddr = mipv6_get_fake_hdr_daddr(udh.daddr, daddr); - - udh.uh.source = sk->sport; - udh.uh.len = len < 0x10000 ? htons(len) : 0; -diff -uprN linux-2.4.25.old/net/netsyms.c linux-2.4.25/net/netsyms.c ---- linux-2.4.25.old/net/netsyms.c 2003-11-28 18:26:21.000000000 +0000 -+++ linux-2.4.25/net/netsyms.c 2004-06-26 11:29:32.000000000 +0100 -@@ -190,6 +190,7 @@ EXPORT_SYMBOL(neigh_sysctl_register); - #endif - EXPORT_SYMBOL(pneigh_lookup); - EXPORT_SYMBOL(pneigh_enqueue); -+EXPORT_SYMBOL(pneigh_delete); - EXPORT_SYMBOL(neigh_destroy); - EXPORT_SYMBOL(neigh_parms_alloc); - EXPORT_SYMBOL(neigh_parms_release); |