commit 338f9a9659807eecb08e7e9cd3488d864da990a7 Author: Arnaud Andre Date: Thu Sep 10 00:45:29 2015 +0200 Initializing uvrrpd repository diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..10926e8 --- /dev/null +++ b/COPYING @@ -0,0 +1,675 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public 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. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. + diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..0c9a3b7 --- /dev/null +++ b/Makefile @@ -0,0 +1,49 @@ +### Makefile +## simple makefile for dev purpose + + +TIME := $(shell date '+%D_%H:%M'| sed 's/\//\\\//g') + +CC := gcc + +CFLAGS += -std=gnu99 -D_GNU_SOURCE\ + -Wall -Wextra -Werror -Wbad-function-cast -Wshadow \ + -Wcast-qual -Wold-style-definition -Wmissing-noreturn \ + -Wstrict-prototypes -Waggregate-return -Wformat=2 \ + -Wundef -Wbad-function-cast -Wunused-parameter -Wnonnull +LDFLAGS += -lrt -Wall + +CFLAGS += -DPATH=\"$(shell pwd)\" + +ifdef DEBUG + CFLAGS += -g -ggdb -DDEBUG + LDFLAGS += +else + CFLAGS += #-Os -fomit-frame-pointer -DNDEBUG +endif + +# select C-files +sources := $(wildcard *.c) +headers := $(wildcard *.h) + +# Get objects from C-files +objects := $(sources:.c=.o) + +uvrrpd: $(objects) + +.PHONY: all +all: uvrrpd + + +INDENT_ARGS := -kr -i8 -c8 -nprs -nce -l80 -cp1 +.PHONY: indent +indent: + @echo "indent $(INDENT_ARGS)" + @indent $(INDENT_ARGS) $(sources) > /dev/null + @indent $(INDENT_ARGS) $(headers) > /dev/null + @find -name "*~" -delete + +.PHONY: clean +clean: + find -name "*.o" -delete + @rm -f uvrrpd diff --git a/bits.h b/bits.h new file mode 100644 index 0000000..a5be71f --- /dev/null +++ b/bits.h @@ -0,0 +1,106 @@ +/* + * bits.h + * + * Copyright (C) 2014 Arnaud Andre + * + * This file is part of uvrrpd. + * + * uvrrpd 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 3 of the License, or + * (at your option) any later version. + * + * uvrrpd is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with uvrrpd. If not, see . + */ + +#ifndef _BITS_H_ +#define _BITS_H_ + +#include +#include +#include +#include +#include +#include + +/** + * BIT_MASK(nr) - bit mask + */ +#define BIT_MASK(nr) (1 << (nr)) + + +/** + * set_bit - Set a bit in memory + */ +static inline void set_bit(int nr, unsigned long *addr) +{ + *addr |= BIT_MASK(nr); +} + +/** + * clear_bit - Clear a bit in memory + */ +static inline void clear_bit(int nr, unsigned long *addr) +{ + *addr &= ~BIT_MASK(nr); +} + +/** + * change_bit - Toggle a bit in memory + */ +static inline void change_bit(int nr, unsigned long *addr) +{ + *addr ^= BIT_MASK(nr); +} + +/** + * test_and_set_bit - Set a bit and return its old value + */ +static inline int test_and_set_bit(int nr, unsigned long *addr) +{ + unsigned long mask = BIT_MASK(nr); + unsigned long old = *addr; + + *addr = old | mask; + return (old & mask) != 0; +} + +/** + * test_and_clear_bit - Clear a bit and return its old value + */ +static inline int test_and_clear_bit(int nr, unsigned long *addr) +{ + unsigned long mask = BIT_MASK(nr); + unsigned long old = *addr; + + *addr = old & ~mask; + return (old & mask) != 0; +} + +/** + * test_and_change_bit - Toggle a bit and return its old value + */ +static inline int test_and_change_bit(int nr, unsigned long *addr) +{ + unsigned long mask = BIT_MASK(nr); + unsigned long old = *addr; + + *addr = old ^ mask; + return (old & mask) != 0; +} + +/** + * test_bit - Determine whether a bit is set + */ +static inline int test_bit(int nr, const unsigned long *addr) +{ + return 1UL & (*addr >> nr); +} + +#endif /* _BITS_H_ */ diff --git a/common.h b/common.h new file mode 100644 index 0000000..ead19f7 --- /dev/null +++ b/common.h @@ -0,0 +1,205 @@ +/* + * common.h - common types and macros + * + * Copyright (C) 2014 Arnaud Andre + * + * This file is part of uvrrpd. + * + * uvrrpd 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 3 of the License, or + * (at your option) any later version. + * + * uvrrpd is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with uvrrpd. If not, see . + */ + + +#ifndef _COMMON_H_ +#define _COMMON_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "log.h" + +/** + * bool - boolean type + */ +typedef enum { + FALSE, + TRUE +} bool; + +/** + * matches( s, c_str ) - Compare strings. + * s Data strings + * c_str C-Strings + */ +#define matches( s, c_str ) \ +({ \ + const char __dummy[] = c_str; \ + (void)(&__dummy); \ + ( memcmp ( s, c_str, sizeof( c_str ) ) == 0 ); \ +}) + +/** + * ARRAY_SIZE() + */ +#define ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0])) + + +/** + * cksum - compute IP checksum + */ +static inline int unsigned short cksum(unsigned short *buf, int nbytes) +{ + uint32_t sum; + uint16_t oddbyte; + + sum = 0; + while (nbytes > 1) { + sum += *buf++; + nbytes -= 2; + } + + if (nbytes == 1) { + oddbyte = 0; + *((uint16_t *) & oddbyte) = *(uint16_t *) buf; + sum += oddbyte; + } + + sum = (sum >> 16) + (sum & 0xffff); + sum += (sum >> 16); + + return (uint16_t) ~ sum; +} + +/** + * mystrtoul - convert a string to an unsigned long int + */ +static inline int mystrtoul(unsigned long *const dest, + const char *const str, unsigned long max) +{ + unsigned long val; + char *endptr; + + errno = 0; + val = strtoull(str, &endptr, 0); + + if ((val == 0 || val == LONG_MAX) && errno == ERANGE) + return -ERANGE; + + if (val > max) + return -ERANGE; + + if (*endptr != '\0') + return -EINVAL; + + *dest = val; + return 0; +} + +/** + * is_file_executable + */ +static inline int is_file_executable(const char *filename) +{ + struct stat sb; + + if (stat(filename, &sb) == -1) { + perror("stat"); + return 0; + } + + if (S_ISREG(sb.st_mode) && + (sb.st_mode & S_IRUSR) && (sb.st_mode & S_IXUSR)) + return 1; + + return 0; +} + + +#define IP4_NMASK 32 +#define IP6_NMASK 128 +/** + * split_ip_netmask() - split IPvX and netmask from a string + */ +static inline int split_ip_netmask(int family, + const char *str, void *addr, + uint8_t * netmask) +{ + char *tmp; + unsigned long ul; + + int netmask_length; + + /* IPv4 */ + if (family == AF_INET) + netmask_length = IP4_NMASK; + + /* IPv6 */ + if (family == AF_INET6) + netmask_length = IP6_NMASK; + + tmp = strstr(str, "/"); + *netmask = 0; + + if (tmp != NULL) { + *tmp = '\0'; + ++tmp; + if (mystrtoul(&ul, tmp, netmask_length) == -ERANGE) { + log_error("%s", "CIDR netmask out of range"); + return -ERANGE; + } + if (netmask != NULL) + *netmask = (uint8_t) ul; + } + + if (inet_pton(family, str, addr) == 0) { + log_error("inet_pton - %s", strerror(errno)); + return -1; + } + + return 0; +} + + +#ifdef DEBUG +/** + * print buf hexa + */ +static inline void print_buf_hexa(const char *str, void *buf, size_t x) +{ + uint8_t i; + int j = 0; + + unsigned char *pbuf = (unsigned char *) buf; + + printf("*** %s ***\n", str); + + for (i = 0; i < x; ++i, ++j) { + if (j == 4) { + printf("\n"); + j = 0; + } + if (j > 0) + printf(":"); + printf("%02X", pbuf[i]); + } + printf("\n"); +} +#endif + +#endif /* _COMMON_H_ */ diff --git a/list.h b/list.h new file mode 100644 index 0000000..5eb520d --- /dev/null +++ b/list.h @@ -0,0 +1,424 @@ +/** + * list.h Copied from the Linux kernel source tree + * + * Implement doubly linked list (list_head) + * + * Licensed under the GPL v2 as per the whole kernel source tree. + * + */ + +#ifndef _LIB_LIST_H_ +#define _LIB_LIST_H_ + + +#include + +/*! + * container_of - cast a member of a descriptor out to the containing descriptor + * + * @ptr: the pointer to the member. + * @type: the type of the container struct this is embedded in. + * @member: the name of the member within the struct. + * + */ +#define container_of(ptr, type, member) ({ \ + typeof( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - offsetof(type,member) );}) + +/*! + * These are non-NULL pointers that will result in page faults + * under normal circumstances, used to verify that nobody uses + * non-initialized list entries. + */ +#define LIST_POISON1 ((void *) 0x00100100) +#define LIST_POISON2 ((void *) 0x00200200) + +/*! + * Simple doubly linked list implementation. + * + * Some of the internal functions ("__xxx") are useful when + * manipulating whole lists rather than single entries, as + * sometimes we already know the next/prev entries and we can + * generate better code by using them directly rather than + * using the generic single-entry routines. + */ + +struct list_head { + struct list_head *next, *prev; +}; + +#define LIST_HEAD_INIT(name) { &(name), &(name) } + +#define LIST_HEAD(name) \ + struct list_head name = LIST_HEAD_INIT(name) + +static inline void INIT_LIST_HEAD(struct list_head *list) +{ + list->next = list; + list->prev = list; +} + +/*! + * Insert a new entry between two known consecutive entries. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void +__list_add(struct list_head *new, + struct list_head *prev, struct list_head *next) +{ + next->prev = new; + new->next = next; + new->prev = prev; + prev->next = new; +} + +/*! + * list_add - add a new entry + * @new: new entry to be added + * @head: list head to add it after + * + * Insert a new entry after the specified head. + * This is good for implementing stacks. + */ +static inline void list_add(struct list_head *new, struct list_head *head) +{ + __list_add(new, head, head->next); +} + +/*! + * list_add_tail - add a new entry + * @new: new entry to be added + * @head: list head to add it before + * + * Insert a new entry before the specified head. + * This is useful for implementing queues. + */ +static inline void list_add_tail(struct list_head *new, struct list_head *head) +{ + __list_add(new, head->prev, head); +} + +/*! + * Delete a list entry by making the prev/next entries + * point to each other. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_del(struct list_head *prev, struct list_head *next) +{ + next->prev = prev; + prev->next = next; +} + +/*! + * list_del - deletes entry from list. + * @entry: the element to delete from the list. + * Note: list_empty on entry does not return true after this, the entry is + * in an undefined state. + */ +static inline void list_del(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + entry->next = LIST_POISON1; + entry->prev = LIST_POISON2; +} + +/*! + * list_del_init - deletes entry from list and reinitialize it. + * @entry: the element to delete from the list. + */ +static inline void list_del_init(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + INIT_LIST_HEAD(entry); +} + +/** + * list_replace - replace old entry by new one + * @old : the element to be replaced + * @new : the new element to insert + * + * If @old was empty, it will be overwritten. + */ +static inline void list_replace(struct list_head *old, struct list_head *new) +{ + new->next = old->next; + new->next->prev = new; + new->prev = old->prev; + new->prev->next = new; +} + +static inline void +list_replace_init(struct list_head *old, struct list_head *new) +{ + list_replace(old, new); + INIT_LIST_HEAD(old); +} + +/*! + * list_swap - swap elements from list + * @a: the entry to move to a + * @b: the entry to move to b + */ +static inline void list_swap(struct list_head *a, struct list_head *b) +{ + if (a->next == b) { + list_replace(a, b); + list_add(b, a); + } + else if (a->prev == b) { + list_replace(b, a); + list_add(a, b); + } + else { + struct list_head tmp; + + tmp.next = b->next; + tmp.prev = b->prev; + list_replace(a, b); + list_replace(&tmp, a); + } +} + +/*! + * list_move - delete from one list and add as another's head + * @list: the entry to move + * @head: the head that will precede our entry + */ +static inline void list_move(struct list_head *list, struct list_head *head) +{ + __list_del(list->prev, list->next); + list_add(list, head); +} + +/*! + * list_move_tail - delete from one list and add as another's tail + * @list: the entry to move + * @head: the head that will follow our entry + */ +static inline void +list_move_tail(struct list_head *list, struct list_head *head) +{ + __list_del(list->prev, list->next); + list_add_tail(list, head); +} + +/** + * list_is_last - tests whether @list is the last entry in list @head + * @list: the entry to test + * @head: the head of the list + */ +static inline int +list_is_last(const struct list_head *list, const struct list_head *head) +{ + return list->next == head; +} + +/*! + * list_empty - tests whether a list is empty + * @head: the list to test. + */ +static inline int list_empty(struct list_head const *head) +{ + return head->next == head; +} + +static inline void __list_splice(struct list_head *list, struct list_head *head) +{ + struct list_head *first = list->next; + struct list_head *last = list->prev; + struct list_head *at = head->next; + + first->prev = head; + head->next = first; + + last->next = at; + at->prev = last; +} + +/*! + * list_splice - join two lists + * @list: the new list to add. + * @head: the place to add it in the first list. + */ +static inline void list_splice(struct list_head *list, struct list_head *head) +{ + if (!list_empty(list)) + __list_splice(list, head); +} + +/*! + * list_splice_init - join two lists and reinitialise the emptied list. + * @list: the new list to add. + * @head: the place to add it in the first list. + * + * The list at @list is reinitialised + */ +static inline void +list_splice_init(struct list_head *list, struct list_head *head) +{ + if (!list_empty(list)) { + __list_splice(list, head); + INIT_LIST_HEAD(list); + } +} + +/*! + * list_entry - get the struct for this entry + * @ptr: the &struct list_head pointer. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_struct within the struct. + */ +#define list_entry(ptr, type, member) \ + container_of(ptr, type, member) + +/** + * list_first_entry - get the first element from a list + * @ptr: the list head to take the element from. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_struct within the struct. + * + * Note, that list is expected to be not empty. + */ +#define list_first_entry(ptr, type, member) \ + list_entry((ptr)->next, type, member) + +/*! + * list_for_each - iterate over a list + * @pos: the &struct list_head to use as a loop counter. + * @head: the head for your list. + */ +#define list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); pos = pos->next) + +/*! + * list_for_each_prev - iterate over a list backwards + * @pos: the &struct list_head to use as a loop counter. + * @head: the head for your list. + */ +#define list_for_each_prev(pos, head) \ + for (pos = (head)->prev; pos != (head); pos = pos->prev) + +/*! + * list_for_each_safe - iterate over a list safe against removal of list entry + * @pos: the &struct list_head to use as a loop counter. + * @n: another &struct list_head to use as temporary storage + * @head: the head for your list. + */ +#define list_for_each_safe(pos, n, head) \ + for (pos = (head)->next, n = pos->next; pos != (head); \ + pos = n, n = pos->next) + +/*! + * list_for_each_entry - iterate over list of given type + * @pos: the type * to use as a loop counter. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry(pos, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = list_entry(pos->member.next, typeof(*pos), member)) + +/*! + * list_for_each_entry_reverse - iterate backwards over list of given type. + * @pos: the type * to use as a loop counter. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry_reverse(pos, head, member) \ + for (pos = list_entry((head)->prev, typeof(*pos), member); \ + &pos->member != (head); \ + pos = list_entry(pos->member.prev, typeof(*pos), member)) + +/** + * list_for_each_entry_continue - continue iteration over list of given type + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + * + * Continue to iterate over list of given type, continuing after + * the current position. + */ +#define list_for_each_entry_continue(pos, head, member) \ + for (pos = list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = list_entry(pos->member.next, typeof(*pos), member)) + +/** + * list_for_each_entry_from - iterate over list of given type from the current point + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + * + * Iterate over list of given type, continuing from current position. + */ +#define list_for_each_entry_from(pos, head, member) \ + for (; &pos->member != (head); \ + pos = list_entry(pos->member.next, typeof(*pos), member)) + +/*! + * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry + * @pos: the type * to use as a loop counter. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry_safe(pos, n, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member), \ + n = list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.next, typeof(*n), member)) + +/** + * list_for_each_entry_safe_continue + * @pos: the type * to use as a loop cursor. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + * + * Iterate over list of given type, continuing after current point, + * safe against removal of list entry. + */ +#define list_for_each_entry_safe_continue(pos, n, head, member) \ + for (pos = list_entry(pos->member.next, typeof(*pos), member), \ + n = list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.next, typeof(*n), member)) + +/** + * list_for_each_entry_safe_from + * @pos: the type * to use as a loop cursor. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + * + * Iterate over list of given type from current point, safe against + * removal of list entry. + */ +#define list_for_each_entry_safe_from(pos, n, head, member) \ + for (n = list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.next, typeof(*n), member)) + +/** + * list_for_each_entry_safe_reverse + * @pos: the type * to use as a loop cursor. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + * + * Iterate backwards over list of given type, safe against removal + * of list entry. + */ +#define list_for_each_entry_safe_reverse(pos, n, head, member) \ + for (pos = list_entry((head)->prev, typeof(*pos), member), \ + n = list_entry(pos->member.prev, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.prev, typeof(*n), member)) + + +#endif /* _LIB_LIST_H_ */ diff --git a/log.c b/log.c new file mode 100644 index 0000000..f536952 --- /dev/null +++ b/log.c @@ -0,0 +1,87 @@ +/* + * log.c + * + * Copyright (C) 2014 Arnaud Andre + * + * This file is part of uvrrpd. + * + * uvrrpd 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 3 of the License, or + * (at your option) any later version. + * + * uvrrpd is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with uvrrpd. If not, see . + */ + +/* ISO C */ +#include +#include +#include + +/* POSIX */ +#include + +#include "common.h" + +#ifdef DEBUG +int __log_trigger = LOG_DEBUG; +#else +int __log_trigger = LOG_NOTICE; +#endif + +int log_trigger(char const *level) +{ + /* + * LOG_ERR error conditions + * LOG_WARNING warning conditions + * LOG_NOTICE normal, but significant, condition + * LOG_INFO informational message + * LOG_DEBUG + */ + if (level == NULL) { + return __log_trigger; + } + else if (matches(level, "err")) { + __log_trigger = LOG_ERR; + } + else if (matches(level, "warning")) { + __log_trigger = LOG_WARNING; + } + else if (matches(level, "notice")) { + __log_trigger = LOG_NOTICE; + } + else if (matches(level, "info")) { + __log_trigger = LOG_INFO; + } + else if (matches(level, "debug")) { + __log_trigger = LOG_DEBUG; + } + + return __log_trigger; +} + +void log_open(char const *app, char const *level) +{ + openlog(app ? app : "-", LOG_PERROR | LOG_PID, LOG_DAEMON); + log_trigger(level); +} + +void log_close(void) +{ + closelog(); +} + +void log_it(int priority, char const *format, ...) +{ + va_list ap; + + va_start(ap, format); + vsyslog(priority, format, ap); + va_end(ap); +} diff --git a/log.h b/log.h new file mode 100644 index 0000000..6b7ae28 --- /dev/null +++ b/log.h @@ -0,0 +1,158 @@ +/* + * log.h + * + * Copyright (C) 2014 Arnaud Andre + * + * This file is part of uvrrpd. + * + * uvrrpd 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 3 of the License, or + * (at your option) any later version. + * + * uvrrpd is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with uvrrpd. If not, see . + */ + +#ifndef _LOG_H_ +#define _LOG_H_ + + +/* ISO C */ +#include +#include + +/* POSIX */ +#include + +/** + * log_trigger (char const *level) + * Set up logs level trigger. If level is NULL return current level trigger + * - err Error conditions + * - warning Warning conditions + * - notice Normal, but significant, condition + * - info Informational message + * - debug Debug message + */ +int log_trigger(char const *level); + +/** + * log_open (char const *app, char const *level) + * Open logs system. Define logs level trigger. + * @app Application name + * @level Logs level + */ +void log_open(char const *app, char const *level); + +/** + * log_close( void ) + * Close logs system. + */ +void log_close(void); + +/** + * log_it( int priority, char const *format, ...) + * Log message if priority is higher than level trigger + * @priority Message + * @format Log message + * @... Arguments list + */ +void log_it(int priority, char const *format, ...) + __attribute__ ((__format__(__printf__, 2, 3))); + +/** + * log_error( fmt, ... ) + * @fmt Log message + * @... Arguments list + */ +#define log_error( fmt, ... ) \ +do { \ + log_it( LOG_ERR, "%s::%s "fmt, \ + "error", __func__, ##__VA_ARGS__ ); \ +} while ( 0 ) + +/** + * log_sys_error( fmt, ... ) + * @fmt Log message + * @... Arguments list + */ +#define log_sys_error( fmt, ... ) \ +do { \ + log_it( LOG_ERR, "%s::%s "fmt" %s", \ + "error", __func__, ##__VA_ARGS__, strerror(errno) ); \ +} while ( 0 ) + +/** + * log_warning( fmt, ... ) + * @fmt Log message + * @... Arguments list + */ +#define log_warning( fmt, ... ) \ +do { \ + log_it( LOG_WARNING, "%s::%s "fmt, \ + "warning", __func__, ##__VA_ARGS__ ); \ +} while ( 0 ) + +/** + * log_notice( ... ) + * @... Log message and arguments list + */ +#define log_notice( ... ) \ +do { \ + log_it( LOG_NOTICE, ##__VA_ARGS__ ); \ +} while ( 0 ) + +/** + * log_info( ... ) + * Log message. Enable in info or higher level + * @... Log message and arguments list + */ +#define log_info( ... ) \ +do { \ + extern int __log_trigger; \ + \ + if ( LOG_INFO <= __log_trigger ) \ + log_it( LOG_INFO, ##__VA_ARGS__ ); \ +} while ( 0 ) + +/** + * log_debug( fmt, ... ) + * Log message. Enable in debug level + * @fmt Log message + * @... Arguments list + */ +#define log_debug( fmt, ... ) \ +do { \ + extern int __log_trigger; \ + \ + if ( LOG_DEBUG <= __log_trigger ) \ + log_it( LOG_DEBUG, "%s %s "fmt, \ + "--- DEBUG ---", __func__, ##__VA_ARGS__ ); \ +} while ( 0 ) + +#ifdef DEBUG +/** + * log_devel( fmt, ... ) + * Log message. Enable in devel level + * @fmt Log message + * @... Arguments list + */ +#define log_devel( fmt, ... ) \ +do { \ + extern int __log_trigger; \ + \ + if ( LOG_DEBUG <= __log_trigger ) \ + log_it( LOG_DEBUG, "%s %s( " fmt " )", \ + "--- DEVEL ---", __func__, ##__VA_ARGS__ ); \ +} while ( 0 ) +#else +#define log_devel( fmt, ... ) +#endif /* DEBUG */ + + +#endif /* _LOG_H_ */ diff --git a/uvrrpd.c b/uvrrpd.c new file mode 100644 index 0000000..03e9e4c --- /dev/null +++ b/uvrrpd.c @@ -0,0 +1,199 @@ +/* + * uvrrpd.c - main entry point, server initialization + * + * Copyright (C) 2014 Arnaud Andre + * + * This file is part of uvrrpd. + * + * uvrrpd 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 3 of the License, or + * (at your option) any later version. + * + * uvrrpd is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with uvrrpd. If not, see . + */ + +#include +#include +#include +#include + +#include "uvrrpd.h" +#include "vrrp.h" +#include "vrrp_net.h" +#include "vrrp_adv.h" +#include "vrrp_arp.h" +#include "vrrp_na.h" +#include "vrrp_options.h" +#include "vrrp_exec.h" + +#include "log.h" + +unsigned long reg = 0UL; +int background = 1; +char *loglevel = NULL; + +static void signal_handler(int sig); +static void signal_setup(void); + +/** + * main() - entry point + * + * Declare VRRP instance, init daemon + * and launch state machine + */ +int main(int argc, char *argv[]) +{ + signal_setup(); + + /* Current VRRP instance */ + struct vrrp vrrp; + struct vrrp_net vnet; + + /* Init VRRP instance */ + vrrp_init(&vrrp); + vrrp_net_init(&vnet); + + /* cmdline options */ + if (! !vrrp_options(&vrrp, &vnet, argc, argv)) + exit(EXIT_FAILURE); + + /* open sockets */ + if ((vrrp_net_socket(&vnet) != 0) || (vrrp_net_socket_xmit(&vnet) != 0)) + exit(EXIT_FAILURE); + + /* hook script */ + if (vrrp_exec_init(&vrrp) != 0) + exit(EXIT_FAILURE); + + /* advertisement pkt */ + if (vrrp_adv_init(&vnet, &vrrp) != 0) + exit(EXIT_FAILURE); + + /* net topology */ + if (vnet.family == AF_INET) { + if (vrrp_arp_init(&vnet) != 0) + exit(EXIT_FAILURE); + } + else if (vnet.family == AF_INET6) { + if (vrrp_na_init(&vnet) != 0) + exit(EXIT_FAILURE); + } + + /* daemonize */ + log_open("uvrrpd", (char const *) loglevel); + if (background) { + daemon(0, (log_trigger(NULL) > LOG_INFO)); + } + else + chdir("/"); + + /* process */ + set_bit(KEEP_GOING, ®); + while (test_bit(KEEP_GOING, ®) && !vrrp_process(&vrrp, &vnet)); + + /* shutdown */ + vrrp_adv_cleanup(&vnet); + + if (vnet.family == AF_INET) + vrrp_arp_cleanup(&vnet); + + vrrp_cleanup(&vrrp); + vrrp_exec_cleanup(&vrrp); + vrrp_net_cleanup(&vnet); + + log_close(); + free(loglevel); + + return EXIT_SUCCESS; +} + + +/** + * signal_handler - Signal handler + */ +static void signal_handler(int sig) +{ + switch (sig) { + case SIGHUP: + log_notice("HUP to the init state"); + set_bit(UVRRPD_RELOAD, ®); + break; + + case SIGUSR1: + case SIGUSR2: + set_bit(UVRRPD_DUMP, ®); + break; + + case SIGPIPE: + log_notice("this is not a SIGPIPE"); + set_bit(UVRRPD_LOGOUT, ®); + break; + + case SIGINT: + case SIGTERM: + case SIGQUIT: + log_notice("%s - exit daemon", strsignal(sig)); + set_bit(UVRRPD_RELOAD, ®); + clear_bit(KEEP_GOING, ®); + break; + + case SIGCHLD: + /* bleh */ + break; + + default: + log_error("%s %d", strsignal(sig), sig); + break; + } +} + +/** + * signal_setup + * - register signal handler + * - SIGTERM: shutdown daemon + * - SIGHUP: reload daemon (switch to init state) + * - SIGCHLD: notify end of task (vrrp_exec()) + * - SIGUSR1: logs daemon context: vrrp_context() + * - SIGUSR2: todo, same as USR1 for the moment + * - SIGPIPE: socket write failure + * + * - blocked signal, unblocked them on select() syscall vrrp_process() + */ +static void signal_setup(void) +{ + struct sigaction sa; + + /* setup signal */ + memset(&sa, 0x00, sizeof(sa)); + sa.sa_handler = signal_handler; + sa.sa_flags = 0; + + sigaction(SIGINT, &sa, NULL); + sigaction(SIGTERM, &sa, NULL); + sigaction(SIGQUIT, &sa, NULL); + sigaction(SIGHUP, &sa, NULL); + sigaction(SIGCHLD, &sa, NULL); + sigaction(SIGUSR1, &sa, NULL); + sigaction(SIGUSR2, &sa, NULL); + sigaction(SIGALRM, &sa, NULL); + sigaction(SIGPIPE, &sa, NULL); + + /* setup signal mask */ + sigemptyset(&sa.sa_mask); + + sigaddset(&sa.sa_mask, SIGINT); + sigaddset(&sa.sa_mask, SIGTERM); + sigaddset(&sa.sa_mask, SIGQUIT); + sigaddset(&sa.sa_mask, SIGHUP); + sigaddset(&sa.sa_mask, SIGUSR1); + sigaddset(&sa.sa_mask, SIGUSR2); + + sigprocmask(SIG_BLOCK, &sa.sa_mask, NULL); +} diff --git a/uvrrpd.h b/uvrrpd.h new file mode 100644 index 0000000..ca8ea73 --- /dev/null +++ b/uvrrpd.h @@ -0,0 +1,42 @@ +/* + * uvrrpd.h + * + * Copyright (C) 2014 Arnaud Andre + * + * This file is part of uvrrpd. + * + * uvrrpd 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 3 of the License, or + * (at your option) any later version. + * + * uvrrpd is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with uvrrpd. If not, see . + */ + +#ifndef _UVRRPD_H_ +#define _UVRRPD_H_ + +#include "bits.h" + +/** + * uvrrpd_control + * Enum server control register flags + */ +enum uvrrpd_control { + /* daemon keep going bit */ + KEEP_GOING = BIT_MASK(0), + /* daemon dump bit */ + UVRRPD_DUMP = BIT_MASK(1), + /* daemon logout bit */ + UVRRPD_LOGOUT = BIT_MASK(2), + /* daemon reload bit */ + UVRRPD_RELOAD = BIT_MASK(3), +}; + +#endif /* _UVRRPD_ */ diff --git a/vrrp.c b/vrrp.c new file mode 100644 index 0000000..8589b7f --- /dev/null +++ b/vrrp.c @@ -0,0 +1,121 @@ +/* + * vrrp.c - init VRRP instance and VRRP state functions + * + * Copyright (C) 2014 Arnaud Andre + * + * This file is part of uvrrpd. + * + * uvrrpd 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 3 of the License, or + * (at your option) any later version. + * + * uvrrpd is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with uvrrpd. If not, see . + */ + +#include + +#include "vrrp.h" +#include "vrrp_net.h" +#include "vrrp_state.h" + +#include "uvrrpd.h" +#include "bits.h" + +#include "log.h" + +extern unsigned long reg; + +/** + * vrrp_init() - init struct vrrp with default values + */ +void vrrp_init(struct vrrp *vrrp) +{ + /* VRRP version */ + vrrp->version = RFC3768; + + vrrp->vrid = 0; + vrrp->priority = PRIO_DFL; + vrrp->naddr = 0; + + /* auth */ + vrrp->auth_type = NOAUTH; + vrrp->auth_data = NULL; + + /* state */ + vrrp->state = INIT; + vrrp->preempt = PREEMPT_DFL; + + /* script */ + vrrp->scriptname = NULL; + vrrp->argv = NULL; + + /* timers */ + vrrp->adv_int = 0; + vrrp->master_adv_int = 0; + vrrp_timer_clear(&vrrp->adv_timer); + vrrp_timer_clear(&vrrp->masterdown_timer); +} + +/** + * vrrp_context() - dump vrrp info + */ +static void vrrp_context(struct vrrp *vrrp) +{ + log_notice("===================="); + log_notice("VRID %d", vrrp->vrid); + log_notice("current_state %s", STR_STATE(vrrp->state)); + log_notice("adv_int %d", vrrp->adv_int); + if (vrrp->version == RFC5798) + log_notice("master_adv_int %d", vrrp->master_adv_int); + log_notice("preempt %s", STR_PREEMPT(vrrp->preempt)); + log_notice("naddr %d", vrrp->naddr); + log_notice("===================="); +} + + +/** + * vrrp_process() - vrrp control and state machine + */ +int vrrp_process(struct vrrp *vrrp, struct vrrp_net *vnet) +{ + switch (vrrp->state) { + case INIT: + vrrp_state_init(vrrp, vnet); + break; + + case BACKUP: + vrrp_state_backup(vrrp, vnet); + break; + + case MASTER: + vrrp_state_master(vrrp, vnet); + break; + + default: + /* invalid state */ + return -ENOSYS; + break; + } + + if (test_and_clear_bit(UVRRPD_DUMP, ®)) + vrrp_context(vrrp); + + return 0; +} + + +/** + * vrrp_cleanup() - clean before exiting + */ +void vrrp_cleanup(struct vrrp *vrrp) +{ + free(vrrp->scriptname); + free(vrrp->auth_data); +} diff --git a/vrrp.h b/vrrp.h new file mode 100644 index 0000000..0c7a6de --- /dev/null +++ b/vrrp.h @@ -0,0 +1,119 @@ +/* + * vrrp.h - define main struct vrrp (VRRP instance) and some + * constants + * + * Copyright (C) 2014 Arnaud Andre + * + * This file is part of uvrrpd. + * + * uvrrpd 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 3 of the License, or + * (at your option) any later version. + * + * uvrrpd is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with uvrrpd. If not, see . + */ + +#ifndef _VRRP_H_ +#define _VRRP_H_ + +#include + +#include "common.h" +#include "vrrp_net.h" +#include "vrrp_timer.h" +#include "vrrp_state.h" + +/* MAX values */ +#define VRID_MAX 255 +#define VRRP_PRIO_MAX 255 +#define ADVINT_MAX 4095 /* RFC5798 */ +#define PRIO_OWNER VRRP_PRIO_MAX + +/* DEFAULT values */ +#define PREEMPT_DFL TRUE +#define PRIO_DFL 100 + +/* External script */ +#define VRRP_SCRIPT PATH "/vrrp_switch.sh" +#define VRRP_SCRIPT_MAX sysconf(_SC_ARG_MAX) + +/* preemption */ +#define STR_PREEMPT(s) (s == TRUE ? "true" : "false") + +/** + * vrrp_version - VRRP version protocol + * @RFC2338 : VRRPv2 (deprecated) + * @RFC3768 : VRRPv2 + * @RFC5798 : VRRPv3 + */ +typedef enum { + RFC2338 = 2, + RFC3768 = 2, + RFC5798 = 3 +} vrrp_version; + +/** + * vrrp_authtype - Authentication type + * (from VRRPv2 / rfc2332) + */ +#define VRRP_AUTH_PASS_LEN 8 +typedef enum { + NOAUTH, + SIMPLE, + HMAC /* not supported */ +} vrrp_authtype; + +/** + * vrrp - Main structure defining VRRP instance + */ +struct vrrp { + vrrp_version version; /* VRRP version */ + uint8_t vrid; /* VRID 1 - 255 */ + uint8_t priority; /* PRIO 0 - 255 */ + uint8_t naddr; /* count ip addresses */ + + /* Advertisement interval + * + * VRRPv2 : + * - delay in s + * - default 1s + * - 8 bits field + * + * VRRPv3 : + * - delay in centisecond + * - default 100cs (1s) + * - 12 bits field + */ + uint16_t adv_int; + + /* Master advertisement interval + * only in VRRPv3 / rfc5798 + */ + uint16_t master_adv_int; + + vrrp_authtype auth_type; + char *auth_data; + + vrrp_state state; + bool preempt; + + char *scriptname; + char **argv; + + struct vrrp_timer adv_timer; + struct vrrp_timer masterdown_timer; +}; + +/* funcs */ +void vrrp_init(struct vrrp *vrrp); +void vrrp_cleanup(struct vrrp *vrrp); +int vrrp_process(struct vrrp *vrrp, struct vrrp_net *vnet); + +#endif /* _VRRP_H_ */ diff --git a/vrrp_adv.c b/vrrp_adv.c new file mode 100644 index 0000000..def1809 --- /dev/null +++ b/vrrp_adv.c @@ -0,0 +1,288 @@ +/* + * vrrp_adv.c - VRRP advertisement + * + * Copyright (C) 2014 Arnaud Andre + * + * This file is part of uvrrpd. + * + * uvrrpd 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 3 of the License, or + * (at your option) any later version. + * + * uvrrpd is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with uvrrpd. If not, see . + */ + +#include +#include +#include +#include // ETH_P_IP = 0x0800, ETH_P_IPV6 = 0x86DD +#include +#include +#include + +#include "log.h" +#include "vrrp.h" +#include "vrrp_net.h" +#include "vrrp_rfc.h" + +/* VRRP multicast group */ +#define INADDR_VRRP_GROUP 0xe0000012 /* 224.0.0.18 */ +#define IN6ADDR_VRRP_GROUP "FF02::12" + +#define ETHDR_SIZE sizeof(struct ether_header) + +#define VRRP_TYPE_ADV 1 + +/** + * ether_header vrrp_adv_eth - VRRP ethernet header + */ +static struct ether_header vrrp_adv_eth = { + .ether_dhost = {0x01, + 0x00, + 0x5e, + (INADDR_VRRP_GROUP >> 16) & 0x7F, + (INADDR_VRRP_GROUP >> 8) & 0xFF, + INADDR_VRRP_GROUP & 0xFF}, + .ether_shost = {0x00, + 0x00, + 0x5e, + 0x00, + 0x01, + 0x00}, /* vrrp->vrid */ +}; + +/** + * vrrp_adv_eth_build() - build VRRP adv ethernet header + */ +static int vrrp_adv_eth_build(struct iovec *iov, const uint8_t vrid, + const int family) +{ + iov->iov_base = malloc(sizeof(struct ether_header)); + struct ether_header *hdr = iov->iov_base; + + if (hdr == NULL) { + log_error("[%d] malloc: %s", vrid, strerror(errno)); + return -1; + } + + memcpy(hdr, &vrrp_adv_eth, sizeof(struct ether_header)); + hdr->ether_shost[5] = vrid; + if (family == AF_INET) + hdr->ether_type = htons(ETH_P_IP); + else /* AF_INET6 */ + hdr->ether_type = htons(ETH_P_IPV6); + + iov->iov_len = ETHDR_SIZE; + + return 0; +} + + +/** + * vrrp_adv_ip4_build() - build VRRP IPv4 advertisement + */ +static int vrrp_adv_ip4_build(struct iovec *iov, const struct vrrp_net *vnet) +{ + iov->iov_base = malloc(sizeof(struct iphdr)); + + struct iphdr *iph = iov->iov_base; + + if (iph == NULL) { + log_error("[%d] malloc: %s", vnet->vrid, strerror(errno)); + return -1; + } + + iph->ihl = 0x5; + iph->version = IPVERSION; + iph->tos = 0x00; + iph->tot_len = htons(IPHDR_SIZE + vnet->adv_getsize(vnet)); + iph->id = htons(0xdead); + iph->ttl = 0xff; /* VRRP_TTL */ + iph->frag_off = 0x00; + iph->protocol = IPPROTO_VRRP; + + iph->saddr = vnet->vif.ipx.addr.s_addr; + iph->daddr = htonl(INADDR_VRRP_GROUP); + + iph->check = cksum((unsigned short *) iph, IPHDR_SIZE); + + iov->iov_len = IPHDR_SIZE; + + return 0; +} + + +/** + * vrrp_adv_ip6_build() - build VRRP IPv6 advertisement + */ +static int vrrp_adv_ip6_build(struct iovec *iov, const struct vrrp_net *vnet) +{ + iov->iov_base = malloc(sizeof(struct ip6_hdr)); + + struct ip6_hdr *ip6h = iov->iov_base; + + if (ip6h == NULL) { + log_error("[%d] malloc: %s", vnet->vrid, strerror(errno)); + return -1; + } + + ip6h->ip6_flow = htonl((6 << 28) | (0 << 20) | 0); + ip6h->ip6_plen = htons(vnet->adv_getsize(vnet)); + ip6h->ip6_nxt = IPPROTO_VRRP; + ip6h->ip6_hlim = 0xff; /* VRRP_TTL */ + + memcpy(&ip6h->ip6_src, &vnet->vif.ip_addr6, sizeof(struct in6_addr)); + + if (inet_pton(AF_INET6, IN6ADDR_VRRP_GROUP, &(ip6h->ip6_dst)) != 1) { + log_error("[%d] inet_pton: %s", vnet->vrid, strerror(errno)); + return -1; + } + + iov->iov_len = sizeof(struct ip6_hdr); + + return 0; +} + + + +/** + * vrrp_net_adv_build() - build VRRP adv pkt + */ +static int vrrp_adv_build(struct iovec *iov, const struct vrrp_net *vnet, + const struct vrrp *vrrp) +{ + iov->iov_base = malloc(vnet->adv_getsize(vnet)); + + struct vrrphdr *pkt = iov->iov_base; + + if (pkt == NULL) { + log_error("[%d] malloc: %s", vnet->vrid, strerror(errno)); + return -1; + } + + pkt->version_type = (vrrp->version << 4) | VRRP_TYPE_ADV; + pkt->vrid = vnet->vrid; + pkt->priority = vrrp->priority; + pkt->naddr = vrrp->naddr; + if (vrrp->version == RFC3768) { + pkt->auth_type = vrrp->auth_type; + pkt->adv_int = vrrp->adv_int; + } + if (vrrp->version == RFC5798) { + pkt->max_adv_int = htons(vrrp->adv_int); + } + + /* write vrrp_ip addresses */ + uint32_t *vip_addr = + (uint32_t *) ((unsigned char *) pkt + VRRP_PKTHDR_SIZE); + + struct vrrp_ip *vip_ptr = NULL; + uint32_t pos = 0; + int naddr = 0; + + list_for_each_entry_reverse(vip_ptr, &vnet->vip_list, iplist) { + if (vnet->family == AF_INET) { + vip_addr[pos] = vip_ptr->ip_addr.s_addr; + ++pos; + } + else { /* AF_INET6 */ + memcpy(&vip_addr[pos], &vip_ptr->ip_addr6, + sizeof(struct in6_addr)); + pos += 4; + } + ++naddr; + if (naddr > vrrp->naddr) { + log_error + ("[%d] Build invalid avd pkt : try to write more vip than expected", + vnet->vrid); + return -1; + } + }; + + /* auth password */ + if ((vrrp->version == RFC2338) && (vrrp->auth_type == SIMPLE) + && (vrrp->auth_data != NULL)) { + uint32_t *auth_data = vip_addr + pos; + memcpy(auth_data, vrrp->auth_data, strlen(vrrp->auth_data)); + } + + /* chksum */ + pkt->chksum = vnet->adv_checksum(vnet, pkt, NULL, NULL); + + /* iov_len */ + iov->iov_len = vnet->adv_getsize(vnet); + + return 0; +} + +/** + * vrrp_adv_send() - send VRRP adv pkt + */ +int vrrp_adv_send(struct vrrp_net *vnet) +{ + return vrrp_net_send(vnet, vnet->__adv, ARRAY_SIZE(vnet->__adv)); +} + +/** + * vrrp_adv_send_zero() - send VRRP adv pkt with priority 0 + */ +int vrrp_adv_send_zero(struct vrrp_net *vnet) +{ + /* get adv buffer */ + struct iovec *iov = &vnet->__adv[2]; + struct vrrphdr *pkt = iov->iov_base; + + /* set priority to 0 and recompute checksum */ + uint8_t priority = pkt->priority; + pkt->priority = 0; + uint16_t chksum = pkt->chksum; + + /* chksum */ + + pkt->chksum = vnet->adv_checksum(vnet, pkt, NULL, NULL); + + /* send adv pkt */ + int ret = vrrp_net_send(vnet, vnet->__adv, ARRAY_SIZE(vnet->__adv)); + + /* restaure original priority and checksum */ + pkt->priority = priority; + pkt->chksum = chksum; + + return ret; +} + +/** + * vrrp_adv_init() - init advertisement pkt to send + */ +int vrrp_adv_init(struct vrrp_net *vnet, const struct vrrp *vrrp) +{ + int status = 0; + + status = vrrp_adv_eth_build(&vnet->__adv[0], vnet->vrid, vnet->family); + if (vnet->family == AF_INET) + status |= vrrp_adv_ip4_build(&vnet->__adv[1], vnet); + else + status |= vrrp_adv_ip6_build(&vnet->__adv[1], vnet); + status |= vrrp_adv_build(&vnet->__adv[2], vnet, vrrp); + + return status; +} + +/** + * vrrp_adv_cleanup() + */ +void vrrp_adv_cleanup(struct vrrp_net *vnet) +{ + /* clean iovec */ + for (int i = 0; i < 3; ++i) { + struct iovec *iov = &vnet->__adv[i]; + free(iov->iov_base); + } +} diff --git a/vrrp_adv.h b/vrrp_adv.h new file mode 100644 index 0000000..bbcc7fd --- /dev/null +++ b/vrrp_adv.h @@ -0,0 +1,79 @@ +/* + * vrrp_adv.h - VRRP advertisement + * + * Copyright (C) 2014 Arnaud Andre + * + * This file is part of uvrrpd. + * + * uvrrpd 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 3 of the License, or + * (at your option) any later version. + * + * uvrrpd is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with uvrrpd. If not, see . + */ + +#ifndef _VRRP_ADV_H_ +#define _VRRP_ADV_H_ + + +int vrrp_adv_init(struct vrrp_net *vnet, const struct vrrp *vrrp); +void vrrp_adv_cleanup(struct vrrp_net *vnet); +int vrrp_adv_send(struct vrrp_net *vnet); +int vrrp_adv_send_zero(struct vrrp_net *vnet); +uint16_t vrrp_adv_chksum(struct vrrp_net *vnet, struct vrrphdr *pkt, + uint32_t saddr, uint32_t daddr); + +uint16_t vrrp_adv_ip6_chksum(struct vrrp_net *vnet, struct vrrphdr *pkt, + struct in6_addr *saddr, struct in6_addr *daddr); + +/** + * vrrp_adv_get_version() - get version_type from received adv pkt + */ +static inline int vrrp_adv_get_version(const struct vrrp_net *vnet) +{ + return vnet->__pkt.adv.version_type; +} + +/** + * vrrp_adv_get_priority() - get priority from received adv priority + */ +static inline int vrrp_adv_get_priority(const struct vrrp_net *vnet) +{ + return vnet->__pkt.adv.priority; +} + +/** + * vrrp_adv_get_ntoa_addr() - return a string ip4 address + */ +static inline char *vrrp_adv_get_ntoa_addr(const struct vrrp_net *vnet) +{ + return inet_ntoa(vnet->__pkt.ip_saddr); +} + +/** + * vrrp_adv_get_ntohl_addr() + */ +static inline uint32_t vrrp_adv_get_ntohl_addr(const struct vrrp_net *vnet) +{ + return ntohl(vnet->__pkt.ip_saddr.s_addr); +} + +/** + * vrrp_adv_get_advint() - get adv interval + */ +static inline uint16_t vrrp_adv_get_advint(const struct vrrp_net *vnet) +{ + if (vnet->__pkt.adv.version_type >> 4 == RFC5798) + return ntohs(vnet->__pkt.adv.max_adv_int); + + return vnet->__pkt.adv.adv_int; + +} +#endif /* _VRRP_ADV_H_ */ diff --git a/vrrp_arp.c b/vrrp_arp.c new file mode 100644 index 0000000..c765af4 --- /dev/null +++ b/vrrp_arp.c @@ -0,0 +1,197 @@ +/* + * vrrp_arp.c + * + * Copyright (C) 2014 Arnaud Andre + * + * This file is part of uvrrpd. + * + * uvrrpd 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 3 of the License, or + * (at your option) any later version. + * + * uvrrpd is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with uvrrpd. If not, see . + */ + +#include +#include +#include +#include // ETH_P_IP = 0x0800, ETH_P_IPV6 = 0x86DD +#include +#include +#include + +#include "log.h" +#include "vrrp.h" +#include "vrrp_net.h" + +#define ETHDR_SIZE sizeof(struct ether_header) + +/** + * ether_header vrrp_arp_eth + */ +static struct ether_header vrrp_arp_eth = { + .ether_dhost = {0xff, 0xff, 0xff, + 0xff, 0xff, 0xff}, + .ether_shost = {0x00, + 0x00, + 0x5e, + 0x00, + 0x01, + 0x00}, /* vrrp->vrid */ +}; + + +#define IP_ALEN 4 +/** + * arphdr_eth - ARP header + */ +struct arphdr_eth { + unsigned char ar_sha[ETH_ALEN]; /* Sender hardware address */ + unsigned char ar_sip[IP_ALEN]; /* Sender IP address */ + unsigned char ar_tha[ETH_ALEN]; /* Target hardware address */ + unsigned char ar_tip[IP_ALEN]; /* Target IP address */ +}; + + +/** + * vrrp_arp_eth_build() + */ +static int vrrp_arp_eth_build(struct iovec *iov, const uint8_t vrid) +{ + iov->iov_base = malloc(sizeof(struct ether_header)); + + struct ether_header *hdr = iov->iov_base; + + if (hdr == NULL) { + log_error("[%d] malloc: %s", vrid, strerror(errno)); + return -1; + } + + memcpy(hdr, &vrrp_arp_eth, sizeof(struct ether_header)); + + hdr->ether_shost[5] = vrid; + hdr->ether_type = htons(ETHERTYPE_ARP); + + iov->iov_len = ETHDR_SIZE; + + return 0; +} + +/** + * vrrp_arp_send() - Send arp gratuitous for each vip + */ +int vrrp_arp_send(struct vrrp_net *vnet) +{ + struct vrrp_ip *vip_ptr = NULL; + + /* we have to send one arp pkt by vip */ + list_for_each_entry_reverse(vip_ptr, &vnet->vip_list, iplist) { + vrrp_net_send(vnet, vip_ptr->__topology, + ARRAY_SIZE(vip_ptr->__topology)); + } + + return 0; +} + +/** + * vrrp_arp_build() - Build arp header + */ +static int vrrp_arp_build(struct iovec *iov, const uint8_t vrid) +{ + iov->iov_base = malloc(sizeof(struct arphdr)); + + struct arphdr *arph = iov->iov_base; + + if (arph == NULL) { + log_error("[%d] malloc: %s", vrid, strerror(errno)); + return -1; + } + + arph->ar_hrd = htons(ARPHRD_ETHER); /* Format of hardware address */ + arph->ar_pro = htons(ETHERTYPE_IP); /* Format of protocol address */ + arph->ar_hln = ETH_ALEN; /* Length of hardware address */ + arph->ar_pln = IP_ALEN; /* Length of protocol address */ + arph->ar_op = htons(ARPOP_REQUEST); /* ARP opcode (command) */ + + iov->iov_len = sizeof(struct arphdr); + + return 0; +} + +/** + * vrrp_arp_vrrp_build() - VRRP arp payload + */ +static int vrrp_arp_vrrp_build(struct iovec *iov, struct vrrp_ip *vip, + struct vrrp_net *vnet) +{ + iov->iov_base = malloc(sizeof(struct arphdr_eth)); + + struct arphdr_eth *arpeth = iov->iov_base; + + if (arpeth == NULL) { + log_error("[%d] malloc: %s", vnet->vrid, strerror(errno)); + return -1; + } + + arpeth->ar_sha[0] = 0x00; + arpeth->ar_sha[1] = 0x00; + arpeth->ar_sha[2] = 0x5e; + arpeth->ar_sha[3] = 0x00; + arpeth->ar_sha[4] = 0x01; + arpeth->ar_sha[5] = vnet->vrid; + + memcpy(arpeth->ar_sip, &vip->ip_addr.s_addr, IP_ALEN); + memset(arpeth->ar_tha, 0xFF, ETH_ALEN); + memcpy(arpeth->ar_tip, &arpeth->ar_sip, IP_ALEN); + + iov->iov_len = sizeof(struct arphdr_eth); + + return 0; +} + +/** + * vrrp_arp_init() + */ +int vrrp_arp_init(struct vrrp_net *vnet) +{ + int status = 0; + + /* we have to build one arp pkt by vip */ + struct vrrp_ip *vip_ptr = NULL; + + list_for_each_entry_reverse(vip_ptr, &vnet->vip_list, iplist) { + status = + vrrp_arp_eth_build(&vip_ptr->__topology[0], vnet->vrid); + status |= vrrp_arp_build(&vip_ptr->__topology[1], vnet->vrid); + status |= + vrrp_arp_vrrp_build(&vip_ptr->__topology[2], vip_ptr, vnet); + } + + return status; +} + +/** + * vrrp_arp_cleanup() + */ +void vrrp_arp_cleanup(struct vrrp_net *vnet) +{ + /* clean arp buffer for each vrrp_ip addr */ + struct vrrp_ip *vip_ptr = NULL; + + list_for_each_entry(vip_ptr, &vnet->vip_list, iplist) { + + /* clean iovec */ + for (int i = 0; i < 3; ++i) { + struct iovec *iov = &vip_ptr->__topology[i]; + free(iov->iov_base); + } + + } +} diff --git a/vrrp_arp.h b/vrrp_arp.h new file mode 100644 index 0000000..54f5304 --- /dev/null +++ b/vrrp_arp.h @@ -0,0 +1,29 @@ +/* + * vrrp_arp.h - ARP for VRRP IPv4 advertisement + * + * Copyright (C) 2014 Arnaud Andre + * + * This file is part of uvrrpd. + * + * uvrrpd 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 3 of the License, or + * (at your option) any later version. + * + * uvrrpd is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with uvrrpd. If not, see . + */ + +#ifndef _VRRP_ARP_H_ +#define _VRRP_ARP_H_ + +int vrrp_arp_init(struct vrrp_net *vnet); +void vrrp_arp_cleanup(struct vrrp_net *vnet); +int vrrp_arp_send(struct vrrp_net *vnet); + +#endif /* _VRRP_ARP_H_ */ diff --git a/vrrp_exec.c b/vrrp_exec.c new file mode 100644 index 0000000..180d902 --- /dev/null +++ b/vrrp_exec.c @@ -0,0 +1,236 @@ +/* + * vrrp_exec.c - call an external script while changing state + * + * Copyright (C) 2014 Arnaud Andre + * + * This file is part of uvrrpd. + * + * uvrrpd 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 3 of the License, or + * (at your option) any later version. + * + * uvrrpd is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with uvrrpd. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "vrrp.h" +#include "vrrp_exec.h" +#include "common.h" +#include "log.h" + +/* SCRIPT_ARG_MAX : bytes of args + * (255 IPv6 addresses might be specified) + * 1 IPv6 = 45 bytes in a string format + */ +#define SCRIPT_ARG_MAX INET6_ADDRSTRLEN * NI_MAXHOST +#define SCRIPT_NARGS 10 + +/** + * vrrp_build_args() - prepare args that will be passed to + * external script + * + * Build a argv array destined to execve() + */ +static int vrrp_build_args(const char *scriptname, char **argv, + const struct vrrp *vrrp, const struct vrrp_net *vnet, + vrrp_state state) +{ + /* get basename from scriptname */ + char *name = strchr(scriptname, '/'); + if (name != NULL) { + name++; + snprintf(argv[0], SCRIPT_ARG_MAX, "%s", name); + } + else + snprintf(argv[0], SCRIPT_ARG_MAX, "%s", scriptname); + + /* List of args passed to script : + * 1. state + * 2. vrid + * 3. ifname + * 4. priority + * 5. adv_int + * 6. naddr + * 7. family + * 8 & more. vipaddrs ... + */ + snprintf(argv[1], SCRIPT_ARG_MAX, "%s", STR_STATE(state)); + snprintf(argv[2], SCRIPT_ARG_MAX, "%d", vrrp->vrid); + snprintf(argv[3], SCRIPT_ARG_MAX, "%s", vnet->vif.ifname); + snprintf(argv[4], SCRIPT_ARG_MAX, "%d", vrrp->priority); + snprintf(argv[5], SCRIPT_ARG_MAX, "%d", vrrp->adv_int); + snprintf(argv[6], SCRIPT_ARG_MAX, "%d", vrrp->naddr); + snprintf(argv[7], SCRIPT_ARG_MAX, "%d", + (vnet->family == AF_INET ? 4 : 6)); + + /* serialize vipaddrs + * ip0,ip1,...,ipn */ + //argv[7] = '\0'; + int argv_ips = SCRIPT_NARGS - 2; + memset(argv[argv_ips], 0, strlen(argv[argv_ips])); + int plen = 0; + struct vrrp_ip *vip_ptr = NULL; + list_for_each_entry_reverse(vip_ptr, &vnet->vip_list, iplist) { + plen = strlen(argv[argv_ips]); + if (plen != 0) + argv[argv_ips][plen] = ','; + + if (vnet->family == AF_INET) + snprintf(argv[argv_ips] + strlen(argv[argv_ips]), + SCRIPT_ARG_MAX - plen + 1, "%s", + inet_ntoa(vip_ptr->ip_addr)); + + if (vnet->family == AF_INET6) { + char straddr[INET6_ADDRSTRLEN]; + snprintf(argv[argv_ips] + strlen(argv[argv_ips]), + SCRIPT_ARG_MAX - plen + 1, "%s", + inet_ntop(AF_INET6, &vip_ptr->ip_addr6, + straddr, INET6_ADDRSTRLEN)); + } + + } + + /* the last elmt must be NULL */ + argv[SCRIPT_NARGS - 1] = NULL; + + return 0; +} + +/** + * vrrp_exec() + */ +int vrrp_exec(struct vrrp *vrrp, const struct vrrp_net *vnet, vrrp_state state) +{ + const char *scriptname; + + if (vrrp->scriptname == NULL) + scriptname = VRRP_SCRIPT; + else + scriptname = vrrp->scriptname; + + if (!is_file_executable(scriptname)) { + log_error("File %s doesn't exist or is not executable", + scriptname); + return -1; + } + + vrrp_build_args(scriptname, vrrp->argv, vrrp, vnet, state); + + /* Sig gestion */ + sigset_t blockmask, origmask; + struct sigaction sa_ignore, sa_origquit, sa_origint, sa_default; + + sigemptyset(&blockmask); /* Block SIGCHLD */ + sigaddset(&blockmask, SIGCHLD); + sigprocmask(SIG_BLOCK, &blockmask, &origmask); + sa_ignore.sa_handler = SIG_IGN; /* Ignore SIGINT and SIGQUIT */ + sa_ignore.sa_flags = 0; + sigemptyset(&sa_ignore.sa_mask); + sigaction(SIGINT, &sa_ignore, &sa_origint); + sigaction(SIGQUIT, &sa_ignore, &sa_origquit); + + /* fork */ + pid_t child = fork(); + int status, savedErrno; + + if (child == -1) { + log_error("fork: %s", strerror(errno)); + vrrp_exec_cleanup(vrrp); + return -1; + } + + /* child */ + if (child == 0) { + sa_default.sa_handler = SIG_DFL; + sa_default.sa_flags = 0; + sigemptyset(&sa_default.sa_mask); + if (sa_origint.sa_handler != SIG_IGN) + sigaction(SIGINT, &sa_default, NULL); + if (sa_origquit.sa_handler != SIG_IGN) + sigaction(SIGQUIT, &sa_default, NULL); + sigprocmask(SIG_SETMASK, &origmask, NULL); + + /* execve */ + execve(scriptname, (char *const *) vrrp->argv, NULL); + + log_error("execve: %s", strerror(errno)); + vrrp_exec_cleanup(vrrp); + return -1; + } + + /* parent */ + if (child > 0) { + + while (waitpid(child, &status, 0) == -1) { + if (errno != EINTR) { /* Error other than EINTR */ + log_error("waitpid: %s", strerror(errno)); + status = -1; + break; /* So exit loop */ + } + } + } + + /* Unblock SIGCHLD, restore dispositions of SIGINT and SIGQUIT */ + savedErrno = errno; /* The following may change 'errno' */ + sigprocmask(SIG_SETMASK, &origmask, NULL); + sigaction(SIGINT, &sa_origint, NULL); + sigaction(SIGQUIT, &sa_origquit, NULL); + errno = savedErrno; + + return status; +} + +/** + * vrrp_exec_init() - init vrrp->argv buffer + */ +int vrrp_exec_init(struct vrrp *vrrp) +{ + vrrp->argv = malloc(sizeof(char *) * SCRIPT_NARGS); + + if (vrrp->argv == NULL) { + log_error("malloc: %s", strerror(errno)); + return -1; + } + + for (int i = 0; i < SCRIPT_NARGS - 1; ++i) { + vrrp->argv[i] = malloc(sizeof(char) * SCRIPT_ARG_MAX); + if (vrrp->argv[i] == NULL) { + log_error("malloc: %s", strerror(errno)); + return -1; + } + bzero(vrrp->argv[i], sizeof(char) * SCRIPT_ARG_MAX); + } + + return 0; +} + +/** + * vrrp_exec_cleanup() - cleanup vrrp->argv buffer + */ +void vrrp_exec_cleanup(struct vrrp *vrrp) +{ + if (vrrp->argv != NULL) { + for (int i = 0; i < SCRIPT_NARGS - 1; ++i) { + free(vrrp->argv[i]); + vrrp->argv[i] = NULL; + } + free(vrrp->argv); + vrrp->argv = NULL; + } +} diff --git a/vrrp_exec.h b/vrrp_exec.h new file mode 100644 index 0000000..5054b5e --- /dev/null +++ b/vrrp_exec.h @@ -0,0 +1,31 @@ +/* + * vrrp_exec.h - export prototype of vrrp_exec() + * + * Copyright (C) 2014 Arnaud Andre + * + * This file is part of uvrrpd. + * + * uvrrpd 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 3 of the License, or + * (at your option) any later version. + * + * uvrrpd is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with uvrrpd. If not, see . + */ + +#ifndef _VRRP_EXEC_H_ +#define _VRRP_EXEC_H_ + +#include "vrrp.h" + +int vrrp_exec(struct vrrp *vrrp, const struct vrrp_net *vnet, vrrp_state state); +int vrrp_exec_init(struct vrrp *vrrp); +void vrrp_exec_cleanup(struct vrrp *vrrp); + +#endif /* _VRRP_EXEC_H_ */ diff --git a/vrrp_ip4.c b/vrrp_ip4.c new file mode 100644 index 0000000..9a454d6 --- /dev/null +++ b/vrrp_ip4.c @@ -0,0 +1,245 @@ +/* + * vrrp_ip4.c - IP4 helpers functions + * + * Copyright (C) 2014 Arnaud Andre + * + * This file is part of uvrrpd. + * + * uvrrpd 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 3 of the License, or + * (at your option) any later version. + * + * uvrrpd is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with uvrrpd. If not, see . + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +/* ifreq + ioctl */ +#include +#include +#include +#include + +#include "vrrp.h" +#include "vrrp_ipx.h" +#include "vrrp_net.h" +#include "vrrp_adv.h" +#include "log.h" +#include "common.h" + +#define VRRP_MGROUP4 "224.0.0.18" + +/** + * pshdr_ip4 - pseudo header IPv4 + */ +struct pshdr_ip4 { + uint32_t saddr; + uint32_t daddr; + uint8_t zero; + uint8_t protocol; + uint16_t len; +}; + +/** + * vrrp_ip4_search_vip() - search one vip in vip list + * if vip is found + * set found = 1 + * _vip_ptr point to vip in vnet->vip_list + */ +#define vrrp_ip4_search_vip(vnet, _vip_ptr, _addr, found) \ + do { \ + if (_addr != NULL) \ + list_for_each_entry_reverse(_vip_ptr, &vnet->vip_list, iplist) { \ + if ( _vip_ptr->ip_addr.s_addr == *_addr) {\ + found = 1; \ + break; \ + } \ + } \ + } while(0) + + +/** + * vrrp_ip4_mgroup() - join IPv4 VRRP multicast group + */ +static int vrrp_ip4_mgroup(struct vrrp_net *vnet) +{ + /* Join VRRP multicast group */ + struct ip_mreq group = { {0}, {0} }; + struct in_addr group_addr = { 0 }; + + if (inet_pton(AF_INET, VRRP_MGROUP4, &group_addr) < 0) { + log_error("vrid %d :: inet_pton - %s", vnet->vrid, + strerror(errno)); + return -1; + } + group.imr_multiaddr.s_addr = group_addr.s_addr; + group.imr_interface.s_addr = vnet->vif.ip_addr.s_addr; + + if (setsockopt + (vnet->socket, IPPROTO_IP, IP_ADD_MEMBERSHIP, &group, + sizeof(struct ip_mreq)) < 0) { + log_error("vrid %d :: setsockopt - %s", vnet->vrid, + strerror(errno)); + return -1; + } + + return 0; +} + +/** + * vrrp_ip4_cmp() - Compare VIP list between received vrrpkt and our instance + * Return 0 if the list is the same, + * the number of different VIP else + */ +static int vrrp_ip4_cmp(struct vrrp_net *vnet, struct vrrphdr *vrrpkt) +{ + /* compare IP address(es) */ + uint32_t *vip_addr = + (uint32_t *) ((unsigned char *) vrrpkt + VRRP_PKTHDR_SIZE); + + uint32_t naddr = 0; + int ndiff = 0; + + for (naddr = 0; naddr < vnet->naddr; ++naddr) { + /* vip in vrrpkt */ + in_addr_t *addr = vip_addr + naddr; + + /* search in vrrp_ip list */ + struct vrrp_ip *vip_ptr = NULL; + int found = 0; + vrrp_ip4_search_vip(vnet, vip_ptr, addr, found); + + if (!found) { + log_warning + ("vrid %d :: Invalid pkt - Virtual IP address unexpected", + vnet->vrid); + ++ndiff; + } + } + return ndiff; +} + +/** + * vrrp_ip4_recv() - Fill vrrp_l3 header from received pkt + */ +static int vrrp_ip4_recv(int sock_fd, struct vrrp_recv *recv, + unsigned char *buf, ssize_t buf_size, int *payload_pos) +{ + ssize_t len; + struct iphdr *ip; + + len = read(sock_fd, buf, buf_size); + ip = (struct iphdr *) buf; + + recv->header.len = ip->ihl << 2; + recv->header.proto = ip->protocol; + recv->header.totlen = ntohs(ip->tot_len); + recv->header.ttl = ip->ttl; + + /* saddr and daddr of the ip header */ + recv->ip_saddr.s_addr = ip->saddr; + recv->ip_daddr.s_addr = ip->daddr; + + /* VRRP adv begin here */ + *payload_pos = recv->header.len; + + return len; +} + +/** + * vrrp_ip4_getsize() - return the current size of vrrp instance + */ +static int vrrp_ip4_getsize(const struct vrrp_net *vnet) +{ + return sizeof(struct vrrphdr) + vnet->naddr * sizeof(uint32_t) + + VRRP_AUTH_SIZE; +} + +/** + * vrrp_ip4_chksum() - compute vrrp adv chksum + */ +static uint16_t vrrp_ip4_chksum(const struct vrrp_net *vnet, + struct vrrphdr *pkt, + union vrrp_ipx_addr *ipx_saddr, + union vrrp_ipx_addr *ipx_daddr) +{ + /* reset chksum */ + pkt->chksum = 0; + + if ((pkt->version_type >> 4) == RFC3768) + return cksum((unsigned short *) pkt, vrrp_ip4_getsize(vnet)); + + if ((pkt->version_type >> 4) == RFC5798) { + const struct iovec *iov_iph = &vnet->__adv[1]; + const struct iphdr *iph = iov_iph->iov_base; + + /* get saddr and daddr */ + uint32_t saddr = 0; + uint32_t daddr = 0; + + if (ipx_saddr != NULL) + saddr = ipx_saddr->addr.s_addr; + if (ipx_daddr != NULL) + daddr = ipx_daddr->addr.s_addr; + + /* pseudo_header ipv4 */ + struct pshdr_ip4 psh = { 0 }; + + /* if saddr and daddr are not specified, use addresses from iphdr */ + psh.saddr = (saddr ? saddr : iph->saddr); + psh.daddr = (daddr ? daddr : iph->daddr); + psh.zero = 0; + psh.protocol = iph->protocol; + psh.len = htons(vrrp_ip4_getsize(vnet)); + + uint32_t psh_size = + sizeof(struct pshdr_ip4) + vrrp_ip4_getsize(vnet); + + unsigned short buf[psh_size / sizeof(short)]; + + memcpy(buf, &psh, sizeof(struct pshdr_ip4)); + memcpy(buf + sizeof(struct pshdr_ip4) / sizeof(short), pkt, + vrrp_ip4_getsize(vnet)); + + return cksum(buf, psh_size); + } + + return 0; +} + + +/** + * vrrp_ip4_setsockopt() - no need to setsockopt() in IPv4 + */ +static int vrrp_ip4_setsockopt( __attribute__ ((unused)) + int sock_fd, __attribute__ ((unused)) + int vrid) +{ + return 0; +} + +/* exported VRRP_IP4 helper */ +struct vrrp_ipx VRRP_IP4 = { + .family = AF_INET, + .setsockopt = vrrp_ip4_setsockopt, + .mgroup = vrrp_ip4_mgroup, + .cmp = vrrp_ip4_cmp, + .recv = vrrp_ip4_recv, + .chksum = vrrp_ip4_chksum, + .getsize = vrrp_ip4_getsize, +}; diff --git a/vrrp_ip6.c b/vrrp_ip6.c new file mode 100644 index 0000000..03de274 --- /dev/null +++ b/vrrp_ip6.c @@ -0,0 +1,324 @@ +/* + * vrrp_ip6.c - IPv6 helpers functions + * + * Copyright (C) 2014 Arnaud Andre + * + * This file is part of uvrrpd. + * + * uvrrpd 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 3 of the License, or + * (at your option) any later version. + * + * uvrrpd is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with uvrrpd. If not, see . + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +/* ifreq + ioctl */ +#include +#include +#include +#include +#include /* NI_MAXHOST */ + +#include "vrrp_ipx.h" +#include "vrrp_net.h" +#include "log.h" +#include "common.h" + +#define VRRP_MGROUP6 "ff02::12" +#define IP6HDR_SIZE sizeof(struct ip6_hdr) + +/** + * pshdr_ip6 - pseudo header IPv6 + */ +struct pshdr_ip6 { + struct in6_addr saddr; + struct in6_addr daddr; + uint32_t len; + uint8_t zeros[3]; + uint8_t next_header; +}; + +/** + * vrrp_ip6_search_vip() - search one vip in vip list + * if vip is found + * set found = 1 + * _vip_ptr point to vip in vnet->vip_list + */ +#define vrrp_ip6_search_vip(vnet, _vip_ptr, _addr, found) \ + do { \ + list_for_each_entry_reverse(_vip_ptr, &vnet->vip_list, iplist) { \ + if (memcmp(&(_vip_ptr->ip_addr6), _addr, sizeof(struct in6_addr)) == 0) { \ + found = 1; \ + break; \ + }\ + } \ + } while(0) + +/** + * vrrp_ip6_setsockopt() - Set socket option + * used to find ancillary data in recvmsg() + * see vrrp_ip6_recv() + */ +static int vrrp_ip6_setsockopt(int socket, int vrid) +{ + int on = 1; + + /* IPV6_RECVPKTINFO */ + if (setsockopt + (socket, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, sizeof(on)) < 0) { + log_error("vrid %d :: setsockopt - %s", vrid, strerror(errno)); + return -1; + } + + /* IPV6_RECVHOPLIMIT */ + if (setsockopt + (socket, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on, sizeof(on)) < 0) { + log_error("vrid %d :: setsockopt - %s", vrid, strerror(errno)); + return -1; + } + + return 0; +} + +/** + * vrrp_net_join_mgroup6() - join IPv6 VRRP multicast group + */ +static int vrrp_ip6_mgroup(struct vrrp_net *vnet) +{ + /* Join VRRP multicast group */ + struct ipv6_mreq group = { IN6ADDR_ANY_INIT, 0 }; + struct in6_addr group_addr = IN6ADDR_ANY_INIT; + + if (inet_pton(AF_INET6, VRRP_MGROUP6, &group_addr) < 0) { + log_error("vrid %d :: inet_pton - %s", vnet->vrid, + strerror(errno)); + return -1; + } + + memcpy(&group.ipv6mr_multiaddr, &group_addr, sizeof(struct in6_addr)); + + group.ipv6mr_interface = if_nametoindex(vnet->vif.ifname); + + if (setsockopt + (vnet->socket, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &group, + sizeof(struct ipv6_mreq)) < 0) { + log_error("vrid %d :: setsockopt - %s", vnet->vrid, + strerror(errno)); + return -1; + } + + return 0; +} + + +/** + * vrrp_ip6_cmp() - Compare VIP list between received vrrpkt and our instance + * Return 0 if the list is the same, + * the number of differente VIP else + */ +static int vrrp_ip6_cmp(struct vrrp_net *vnet, struct vrrphdr *vrrpkt) +{ + uint32_t *vip_addr = + (uint32_t *) ((unsigned char *) vrrpkt + VRRP_PKTHDR_SIZE); + +#ifdef DEBUG + print_buf_hexa("vrrp_net_vip6_check vip_addr", vip_addr, + sizeof(struct in6_addr)); +#endif + + uint32_t pos = 0; + int naddr = 0; + int ndiff = 0; + + while (naddr < vnet->naddr) { + /* vip in vrrpkt */ + struct in6_addr *vip = (struct in6_addr *) (vip_addr + pos); + + /* search in vrrp_ip list */ + struct vrrp_ip *vip_ptr = NULL; + int found = 0; + + vrrp_ip6_search_vip(vnet, vip_ptr, vip->s6_addr, found); + + if (!found) { + char host[NI_MAXHOST]; + log_warning + ("vrid %d :: Invalid pkt - Virtual IPv6 address unexpected %s", + vnet->vrid, inet_ntop(vnet->family, vip, host, + sizeof(host))); + ++ndiff; + } + pos += 4; + ++naddr; + } + return ndiff; +} + +/** + * find_ancillary() - search & find IPv6 ancillary data after + * receiving data in a struct msghdr msg (recvmsg()) + */ +static inline void *find_ancillary(struct msghdr *msg, int cmsg_type) +{ + struct cmsghdr *cmsg = NULL; + + for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL; + cmsg = CMSG_NXTHDR(msg, cmsg)) { + if ((cmsg->cmsg_level == IPPROTO_IPV6) + && (cmsg->cmsg_type == cmsg_type)) { + return (CMSG_DATA(cmsg)); + } + } + + return NULL; +} + +/** + * vrrp_ip6_recv() - Fill vrrp_ipx_header from received pkt + */ +static int vrrp_ip6_recv(int sock_fd, struct vrrp_recv *recv, + unsigned char *buf, ssize_t buf_size, int *payload_pos) +{ + ssize_t len; + + /* IPv6 raw sockets return no IP header. We must query + * src/dest via socket options/ancillary data */ + struct msghdr msg; + struct sockaddr_in6 src; + struct iovec iov; + uint8_t ancillary[64]; + + msg.msg_name = &src; + msg.msg_namelen = sizeof(src); + iov.iov_base = buf; + iov.iov_len = buf_size; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = ancillary; + msg.msg_controllen = sizeof(ancillary); + msg.msg_flags = 0; + + len = recvmsg(sock_fd, &msg, 0); + + if (len < 0) { + log_error("recvmsg - %s", strerror(errno)); + return -1; + } + + /* SRC ADDRESS */ + memcpy(&recv->ip_saddr6, &src.sin6_addr, sizeof(struct in6_addr)); + + uint8_t *opt; + + /* HOPLIMIT */ + opt = find_ancillary(&msg, IPV6_HOPLIMIT); + if (opt == NULL) { + log_error("recvmsg - unknown hop limit"); + return -1; + } + + recv->header.ttl = *(int *) opt; + + /* DST ADDRESS */ + opt = find_ancillary(&msg, IPV6_PKTINFO); + if (opt == NULL) { + log_error("recvmsg - unknown dst address"); + return -1; + } + + struct in6_pktinfo *pktinfo = (struct in6_pktinfo *) opt; + + memcpy(&recv->ip_daddr6, &pktinfo->ipi6_addr, sizeof(struct in6_addr)); + + recv->header.len = sizeof(struct ip6_hdr); + recv->header.totlen = recv->header.len + len; + /* kludge, we force it since we have no way to read it in recvmsg() */ + recv->header.proto = IPPROTO_VRRP; + + /* buf is directly filled with VRRP adv + * no need to skip IPv6 header */ + *payload_pos = 0; + + return len; +} + +/** + * vrrp_ip6_getsize() - return the current size of vrrp instance + */ +static int vrrp_ip6_getsize(const struct vrrp_net *vnet) +{ + return sizeof(struct vrrphdr) + vnet->naddr * sizeof(struct in6_addr); +} + +/** + * vrrp_ip6_chksum() - compute VRRP adv chksum + */ +uint16_t vrrp_ip6_chksum(const struct vrrp_net *vnet, struct vrrphdr *pkt, + union vrrp_ipx_addr *ipx_saddr, + union vrrp_ipx_addr *ipx_daddr) +{ + /* reset chksum */ + pkt->chksum = 0; + + const struct iovec *iov_iph = &vnet->__adv[1]; + const struct ip6_hdr *iph = iov_iph->iov_base; + + /* pseudo_header ipv6 */ + struct pshdr_ip6 psh = { IN6ADDR_ANY_INIT, + IN6ADDR_ANY_INIT, + 0, + {0, 0, 0}, + 0 + }; + + if ((ipx_saddr != NULL) && (ipx_daddr != NULL)) { + memcpy(&psh.saddr, &ipx_saddr->addr6, sizeof(struct in6_addr)); + memcpy(&psh.daddr, &ipx_daddr->addr6, sizeof(struct in6_addr)); + } + else { + memcpy(&psh.saddr, &iph->ip6_src, sizeof(struct in6_addr)); + memcpy(&psh.daddr, &iph->ip6_dst, sizeof(struct in6_addr)); + } + + + bzero(&psh.zeros, sizeof(psh.zeros)); + psh.next_header = IPPROTO_VRRP; + psh.len = htons(vrrp_ip6_getsize(vnet)); + + uint32_t psh_size = sizeof(struct pshdr_ip6) + vrrp_ip6_getsize(vnet); + unsigned short buf[psh_size / sizeof(short)]; + + memcpy(buf, &psh, sizeof(struct pshdr_ip6)); + memcpy(buf + sizeof(struct pshdr_ip6) / sizeof(short), pkt, + vrrp_ip6_getsize(vnet)); + + return cksum(buf, psh_size); +} + +/* exported VRRP_IP6 helper */ +struct vrrp_ipx VRRP_IP6 = { + .family = AF_INET6, + .setsockopt = vrrp_ip6_setsockopt, + .mgroup = vrrp_ip6_mgroup, + .cmp = vrrp_ip6_cmp, + .recv = vrrp_ip6_recv, + .chksum = vrrp_ip6_chksum, + .getsize = vrrp_ip6_getsize, +}; diff --git a/vrrp_ipx.h b/vrrp_ipx.h new file mode 100644 index 0000000..dac198c --- /dev/null +++ b/vrrp_ipx.h @@ -0,0 +1,93 @@ +/** + * vrrp_ipx.h + * + * Copyright (C) 2014 Arnaud Andre + * + * This file is part of uvrrpd. + * + * uvrrpd 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 3 of the License, or + * (at your option) any later version. + * + * uvrrpd is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with uvrrpd. If not, see . + */ + +#ifndef _VRRP_IPX_ +#define _VRRP_IPX_ + + +/* from vrrp_net.h */ +struct vrrp_net; +struct vrrphdr; +struct vrrp_recv; + +/** + * struct vrrp_ipx_header - IPvX header informations from received adv pkt + */ +struct vrrp_ipx_header { + int len; + int proto; + int totlen; + int ttl; +}; + +/** + * union vrrp_ipx_addr - IPv4 and IPv6 addr struct + */ +union vrrp_ipx_addr { + struct in_addr addr; + struct in6_addr addr6; +}; +#define ip_addr ipx.addr +#define ip_addr6 ipx.addr6 +#define ip_saddr s_ipx.addr +#define ip_daddr d_ipx.addr +#define ip_saddr6 s_ipx.addr6 +#define ip_daddr6 d_ipx.addr6 + +/** + * struct vrrp_ipx - Helper functions + */ +struct vrrp_ipx { + int family; + int (*setsockopt) (int, int); + int (*mgroup) (struct vrrp_net *); + int (*cmp) (struct vrrp_net *, struct vrrphdr *); + int (*recv) (int, struct vrrp_recv *, unsigned char *, ssize_t, int *); + uint16_t(*chksum) (const struct vrrp_net *, struct vrrphdr *, + union vrrp_ipx_addr *, union vrrp_ipx_addr *); + int (*getsize) (const struct vrrp_net *); +}; +#define set_sockopt ipx_helper->setsockopt +#define join_mgroup ipx_helper->mgroup +#define vip_compare ipx_helper->cmp +#define pkt_receive ipx_helper->recv +#define adv_checksum ipx_helper->chksum +#define adv_getsize ipx_helper->getsize + +extern struct vrrp_ipx VRRP_IP4; /* IPv4 module */ +extern struct vrrp_ipx VRRP_IP6; /* IPv6 module */ + +/** + * vrrp_ipx_set() - Set l3 helper for vrrp_net + */ +static inline struct vrrp_ipx *vrrp_ipx_set(int family) +{ + if (family == AF_INET) { + return &VRRP_IP4; + } + if (family == AF_INET6) { + return &VRRP_IP6; + } + + return NULL; +} + +#endif /* _VRRP_IPX_ */ diff --git a/vrrp_na.c b/vrrp_na.c new file mode 100644 index 0000000..9b2e839 --- /dev/null +++ b/vrrp_na.c @@ -0,0 +1,241 @@ +/* + * vrrp_na.c - Neighbour Advertisement + * + * TODO : need work to use chksum helper in vrrp_ip6.c + * + * Copyright (C) 2014 Arnaud Andre + * + * This file is part of uvrrpd. + * + * uvrrpd 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 3 of the License, or + * (at your option) any later version. + * + * uvrrpd is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with uvrrpd. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include // struct nd_neighbor_advert + +#include "log.h" +#include "vrrp.h" +#include "vrrp_net.h" + +#define IN6ADDR_MCAST "ff02::1" + +#define ETHDR_SIZE sizeof(struct ether_header) +/** + * ether_header vrrp_na_eth + */ +static struct ether_header vrrp_na_eth = { + .ether_dhost = {0x33, 0x33, 0x00, + 0x00, 0x00, 0x01}, + .ether_shost = {0x00, + 0x00, + 0x52, + 0x00, + 0x01, + 0x00}, /* vrrp->vrid */ +}; + + +/** + * pshdr_ip6 - pseudo header IPv6 + */ +struct pshdr_ip6 { + struct in6_addr saddr; + struct in6_addr daddr; + uint32_t len; + uint8_t zeros[3]; + uint8_t next_header; +}; + +/** + * vrrp_na_eth_build() + */ +static int vrrp_na_eth_build(struct iovec *iov, const uint8_t vrid) +{ + iov->iov_base = malloc(sizeof(struct ether_header)); + + struct ether_header *hdr = iov->iov_base; + + if (hdr == NULL) { + log_error("[%d] malloc: %s", vrid, strerror(errno)); + return -1; + } + + memcpy(hdr, &vrrp_na_eth, sizeof(struct ether_header)); + + hdr->ether_shost[5] = vrid; + hdr->ether_type = htons(ETH_P_IPV6); + + iov->iov_len = ETHDR_SIZE; + + return 0; +} + +/** + * vrrp_na_ip6_build() + */ +static int vrrp_na_ip6_build(struct iovec *iov, struct vrrp_ip *ip, + const struct vrrp_net *vnet) +{ + iov->iov_base = malloc(sizeof(struct ip6_hdr)); + + struct ip6_hdr *ip6h = iov->iov_base; + + if (ip6h == NULL) { + log_error("[%d] malloc: %s", vnet->vrid, strerror(errno)); + return -1; + } + + ip6h->ip6_flow = htonl((6 << 28) | (0 << 20) | 0); + ip6h->ip6_plen = htons(sizeof(struct nd_neighbor_advert)); + ip6h->ip6_nxt = IPPROTO_ICMPV6; + ip6h->ip6_hlim = 0xff; + + memcpy(&ip6h->ip6_src, &ip->ip_addr6, sizeof(struct in6_addr)); + + if (inet_pton(AF_INET6, IN6ADDR_MCAST, &(ip6h->ip6_dst)) != 1) { + log_error("[%d] inet_pton: %s", vnet->vrid, strerror(errno)); + return -1; + } + + iov->iov_len = sizeof(struct ip6_hdr); + + return 0; +} + + +/** + * vrrp_na_chksum() - compute na chksum + */ +uint16_t vrrp_na_chksum(struct iovec * iov, struct nd_neighbor_advert * na) +{ + /* reset chksum */ + na->nd_na_hdr.icmp6_cksum = 0; + + const struct ip6_hdr *iph = iov->iov_base; + + /* pseudo_header ipv6 */ + struct pshdr_ip6 psh = { IN6ADDR_ANY_INIT, /* saddr */ + IN6ADDR_ANY_INIT, /* daddr */ + 0, /* length */ + {0, 0, 0}, /* zeros[3] */ + 0 /* next_header */ + }; + + memcpy(&psh.saddr, &iph->ip6_src, sizeof(struct in6_addr)); + memcpy(&psh.daddr, &iph->ip6_dst, sizeof(struct in6_addr)); + + bzero(&psh.zeros, sizeof(psh.zeros)); + psh.next_header = IPPROTO_ICMPV6; + psh.len = htons(sizeof(struct nd_neighbor_advert)); + + uint32_t psh_size = + sizeof(struct pshdr_ip6) + sizeof(struct nd_neighbor_advert); + unsigned short buf[psh_size / sizeof(short)]; + + memcpy(buf, &psh, sizeof(struct pshdr_ip6)); + memcpy(buf + sizeof(struct pshdr_ip6) / sizeof(short), na, + sizeof(struct nd_neighbor_advert)); + + return cksum(buf, psh_size); +} + +/** + * vrrp_na_build() + */ +static int vrrp_na_build(struct iovec *iov, struct vrrp_ip *ip, + const struct vrrp_net *vnet) +{ + iov->iov_base = malloc(sizeof(struct nd_neighbor_advert)); + + struct nd_neighbor_advert *na = iov->iov_base; + + if (na == NULL) { + log_error("[%d] malloc: %s", vnet->vrid, strerror(errno)); + return -1; + } + + na->nd_na_hdr.icmp6_type = ND_NEIGHBOR_ADVERT; + na->nd_na_hdr.icmp6_code = 0; + na->nd_na_hdr.icmp6_cksum = 0; + /* Set R/S/O flags as = R=1, S=0, O=1 (RFC 5798, 6.4.2.(395)) */ + na->nd_na_flags_reserved = ND_NA_FLAG_ROUTER | ND_NA_FLAG_OVERRIDE; + memcpy(&na->nd_na_target, &ip->ip_addr6, sizeof(struct in6_addr)); + + na->nd_na_hdr.icmp6_cksum = vrrp_na_chksum(&ip->__topology[1], na); + + iov->iov_len = sizeof(struct nd_neighbor_advert); + + return 0; +} + +/** + * vrrp_na_send() - for each vip send an unsollicited neighbor advertisement + */ +int vrrp_na_send(struct vrrp_net *vnet) +{ + struct vrrp_ip *vip_ptr = NULL; + + /* we have to send one na by vip */ + list_for_each_entry_reverse(vip_ptr, &vnet->vip_list, iplist) { + vrrp_net_send(vnet, vip_ptr->__topology, + ARRAY_SIZE(vip_ptr->__topology)); + } + + return 0; +} + +/** + * vrrp_na_init() + */ +int vrrp_na_init(struct vrrp_net *vnet) +{ + int status = 0; + + /* we have to build one na pkt by vip */ + struct vrrp_ip *vip_ptr = NULL; + + list_for_each_entry_reverse(vip_ptr, &vnet->vip_list, iplist) { + status = vrrp_na_eth_build(&vip_ptr->__topology[0], vnet->vrid); + status |= + vrrp_na_ip6_build(&vip_ptr->__topology[1], vip_ptr, vnet); + status |= vrrp_na_build(&vip_ptr->__topology[2], vip_ptr, vnet); + } + + return status; +} + + +/** + * vrrp_na_cleanup() + */ +void vrrp_na_cleanup(struct vrrp_net *vnet) +{ + /* clean na buffer for each vrrp_ip addr */ + struct vrrp_ip *vip_ptr = NULL; + + list_for_each_entry(vip_ptr, &vnet->vip_list, iplist) { + + /* clean iovec */ + for (int i = 0; i < 3; ++i) { + struct iovec *iov = &vip_ptr->__topology[i]; + free(iov->iov_base); + } + + } +} diff --git a/vrrp_na.h b/vrrp_na.h new file mode 100644 index 0000000..a61c4ca --- /dev/null +++ b/vrrp_na.h @@ -0,0 +1,29 @@ +/* + * vrrp_na.h - Neighbour Advertisement + * + * Copyright (C) 2014 Arnaud Andre + * + * This file is part of uvrrpd. + * + * uvrrpd 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 3 of the License, or + * (at your option) any later version. + * + * uvrrpd is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with uvrrpd. If not, see . + */ + +#ifndef _VRRP_NA_H_ +#define _VRRP_NA_H_ + +int vrrp_na_init(struct vrrp_net *vnet); +void vrrp_na_cleanup(struct vrrp_net *vnet); +int vrrp_na_send(struct vrrp_net *vnet); + +#endif /* _VRRP_NA_H_ */ diff --git a/vrrp_net.c b/vrrp_net.c new file mode 100644 index 0000000..7bb9427 --- /dev/null +++ b/vrrp_net.c @@ -0,0 +1,544 @@ +/* + * vrrp_net.c - net layer + * + * Copyright (C) 2014 Arnaud Andre + * + * This file is part of uvrrpd. + * + * uvrrpd 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 3 of the License, or + * (at your option) any later version. + * + * uvrrpd is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with uvrrpd. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +/* ifreq + ioctl */ +#include +#include +/* pselect() */ +#include +#include +#include +#include +#include /* NI_MAXHOST */ + +#include "vrrp.h" +#include "vrrp_net.h" +#include "vrrp_timer.h" +#include "vrrp_adv.h" + +#include "common.h" +#include "list.h" +#include "log.h" + +#include "linux/types.h" + + + + +#define VRRP_MGROUP4 "224.0.0.18" +#define VRRP_MGROUP6 "ff02::12" +#define VRRP_TTL 255 + +static inline void vrrp_net_invalidate_buffer(struct vrrp_net *vnet); + +/** + * vrrp_net_init() - init struct vrrp_net of vrrp instance + */ +void vrrp_net_init(struct vrrp_net *vnet) +{ + vnet->vrid = 0; + vnet->naddr = 0; + vnet->socket = 0; + vnet->xmit = 0; + vnet->family = AF_INET; + vnet->ipx_helper = NULL; + + /* init VRRP IPs list */ + INIT_LIST_HEAD(&vnet->vip_list); + + /* init vrrp interface */ + bzero((void *) &vnet->vif, sizeof(struct vrrp_if)); + + /* init pkt buffer */ + bzero((void *) &vnet->__pkt, sizeof(struct vrrp_recv)); +} + +/** + * vrrp_net_cleanup() - cleanup struct vrrp_net + */ +void vrrp_net_cleanup(struct vrrp_net *vnet) +{ + /* clean VIP addr */ + struct vrrp_ip *vip_ptr = NULL; + struct vrrp_ip *n = NULL; + list_for_each_entry_safe(vip_ptr, n, &vnet->vip_list, iplist) + free(vip_ptr); + + free(vnet->vif.ifname); + + /* close sockets */ + close(vnet->socket); + close(vnet->xmit); +} + +/** + * vrrp_net_socket() - create VRRP socket destined to receive VRRP pkt + */ +int vrrp_net_socket(struct vrrp_net *vnet) +{ + /* Open RAW socket */ + vnet->socket = socket(vnet->family, SOCK_RAW, IPPROTO_VRRP); + + if (vnet->socket < 0) { + log_error("vrid %d :: socket - %s", vnet->vrid, + strerror(errno)); + return -1; + } + + vnet->ipx_helper = vrrp_ipx_set(vnet->family); + + if (vnet->ipx_helper == NULL) { + log_error("family not valid"); + return -1; + } + + int status = -1; + + status = vnet->set_sockopt(vnet->socket, vnet->vrid); + status |= vnet->join_mgroup(vnet); + + return status; +} + +/** + * vrrp_net_socket_xmit() - open raw VRRP xmit socket + */ +int vrrp_net_socket_xmit(struct vrrp_net *vnet) +{ + vnet->xmit = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); + + if (vnet->xmit < 0) { + log_error("vrid %d :: socket xmit - %s", vnet->vrid, + strerror(errno)); + return -1; + } + + return 0; +} + + +/** + * vrrp_net_vif_getaddr() - get IPvX addr from a VRRP interface + */ +int vrrp_net_vif_getaddr(struct vrrp_net *vnet) +{ + + struct ifaddrs *ifaddr, *ifa; + int family; + + if (getifaddrs(&ifaddr) == -1) { + log_error("vrid %d :: getifaddrs - %s", vnet->vrid, + strerror(errno)); + return -1; + } + + /* search address */ + for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { + if (strcmp(ifa->ifa_name, vnet->vif.ifname) != 0) + continue; + + if (ifa->ifa_addr == NULL) + continue; + + family = ifa->ifa_addr->sa_family; + + if (family != vnet->family) + continue; + + if (vnet->family == AF_INET) { + vnet->vif.ip_addr.s_addr = + ((struct sockaddr_in *) ifa->ifa_addr)-> + sin_addr.s_addr; + } + else { /* AF_INET6 */ + struct sockaddr_in6 *src = + (struct sockaddr_in6 *) ifa->ifa_addr; + memcpy(&vnet->vif.ip_addr6, &src->sin6_addr, + sizeof(struct in6_addr)); + } + } + + freeifaddrs(ifaddr); + + vrrp_net_vif_mtu(vnet); + + return 0; +} + +/** + * vrrp_net_vif_mtu() - get MTU of vrrp interface + */ +int vrrp_net_vif_mtu(struct vrrp_net *vnet) +{ + struct ifreq ifr; + + strncpy(ifr.ifr_name, vnet->vif.ifname, IFNAMSIZ - 1); + + int fd = socket(AF_INET, SOCK_DGRAM, 0); + + ifr.ifr_addr.sa_family = AF_INET; + + if (ioctl(fd, SIOCGIFMTU, &ifr) < 0) { + log_error("vrid %d :: ioctl - %s", vnet->vrid, strerror(errno)); + return -1; + } + + vnet->vif.mtu = ifr.ifr_mtu; + log_debug("%s mtu : %d", vnet->vif.ifname, vnet->vif.mtu); + + close(fd); + + return 0; +} + + +/** + * vrrp_net_vip_set() - register VRRP virtual IPvX addresses + */ +int vrrp_net_vip_set(struct vrrp_net *vnet, const char *ip) +{ + struct vrrp_ip *vip = malloc(sizeof(struct vrrp_ip)); + + if (vip == NULL) { + log_error("vrid %d :: malloc - %s", vnet->vrid, + strerror(errno)); + return -1; + } + + /* split ip / netmask */ + int status = -1; + + if (vnet->family == AF_INET) + status = + split_ip_netmask(vnet->family, ip, &vip->ip_addr, + &vip->netmask); + + if (vnet->family == AF_INET6) + status = + split_ip_netmask(vnet->family, ip, &vip->ip_addr6, + &vip->netmask); + + if (status != 0) { + fprintf(stderr, "vrid %d :: invalid IP addr %s", vnet->vrid, + ip); + free(vip); + return -1; + } + + list_add_tail(&vip->iplist, &vnet->vip_list); + + return 0; +} + +/** + * vrrp_net_listen() - Wait for a VRRP pkt on vnet->socket + * + * @return TIMER if current timer is expired + * @return PKT else + */ +int vrrp_net_listen(struct vrrp_net *vnet, struct vrrp *vrrp) +{ + struct vrrp_timer *vt; + + /* Check which timer is running + * Advertisement timer or Masterdown timer ? + */ + if (vrrp_timer_is_running(&vrrp->adv_timer)) { + log_debug("vrid %d :: adv_timer is running", vrrp->vrid); + vt = &vrrp->adv_timer; + } + else if (vrrp_timer_is_running(&vrrp->masterdown_timer)) { + log_debug("vrid %d :: masterdown_timer is running", vrrp->vrid); + vt = &vrrp->masterdown_timer; + } + else { /* No timer ? ... exit */ + log_error("vrid %d :: no timer running !", vrrp->vrid); + /* TODO die() */ + exit(EXIT_FAILURE); + } + + /* update timer before pselect() */ + if (vrrp_timer_update(vt)) { + log_debug("vrid %d :: timer expired before pselect", + vrrp->vrid); + /* timer expired or invalid */ + return TIMER; + } + + /* pselect */ + fd_set readfds; + FD_ZERO(&readfds); + FD_SET(vnet->socket, &readfds); + + sigset_t emptyset; + sigemptyset(&emptyset); + + /* Wait for packet or timer expiration */ + if (pselect + (vnet->socket + 1, &readfds, NULL, NULL, + (const struct timespec *) &vt->delta, &emptyset) >= 0) { + + + /* Timer is expired */ + if (vrrp_timer_is_expired(vt)) { + log_debug("vrid %d :: timer expired", vrrp->vrid); + return TIMER; + } + + /* Else we have received a pkt */ + log_debug("vrid %d :: VRRP pkt received", vrrp->vrid); + + /* check if received is valid or not */ + if (vrrp_net_recv(vnet, vrrp) > 0) + return PKT; + else { + log_error("vrid %d :: %s", vrrp->vrid, + "Received an invalid packet"); + return INVALID; + } + } + else { /* Signal or pselect error */ + if (errno == EINTR) { + log_debug("vrid %d :: signal caught", vrrp->vrid); + + return SIGNAL; + } + + log_error("vrid %d :: pselect - %s %d", vrrp->vrid, + strerror(errno), errno); + } + + return INVALID; +} + +/** + * vrrp_net_invalidate_buffer() + * invalidate internal buffer used to stock received pkt + * vnet->__pkt + */ +static inline void vrrp_net_invalidate_buffer(struct vrrp_net *vnet) +{ + vnet->__pkt.adv.version_type = 0; +} + +/** + * vrrp_net_recv() - read and check a received VRRP pkt advertisement + * + * @return len of VRRP pkt if valid, -1 if invalid + */ +int vrrp_net_recv(struct vrrp_net *vnet, const struct vrrp *vrrp) +{ + /* fetch pkt data received to buf */ + unsigned char buf[IP_MAXPACKET]; + + /* read IPvX header values and fill vrrp_recv buffer __pkt */ + int payload_pos = 0; + ssize_t len = vnet->pkt_receive(vnet->socket, &vnet->__pkt, buf, + IP_MAXPACKET, &payload_pos); + + /* check len */ + if (len == -1) { + log_error("vrid %d :: invalid pkt", vnet->vrid); + return INVALID; + } + + if ((len > vnet->vif.mtu) || (len < vnet->adv_getsize(vnet))) { + log_error("vrid %d :: invalid pkt len", vnet->vrid); + return INVALID; + } + + /* read and check vrrp advertisement pkt */ + struct vrrphdr *vrrpkt; + + vrrpkt = (struct vrrphdr *) (buf + payload_pos); + + /* TODO : is this really necessary ?? */ + vrrp_net_invalidate_buffer(vnet); + + /* check VRRP pkt size (including VRRP IP address(es) and Auth data) */ + unsigned int payload_size = + vnet->__pkt.header.totlen - vnet->__pkt.header.len; + + if ((payload_size < VRRP_PKT_MINSIZE) + || (payload_size > VRRP_PKT_MAXSIZE)) { + log_info + ("vrid %d :: Invalid pkt - Invalid packet size %d, expecting size between %ld and %ld", + vrrp->vrid, payload_size, VRRP_PKT_MINSIZE, + VRRP_PKT_MAXSIZE); + return INVALID; + } + + /* verify ip proto */ + if (vnet->__pkt.header.proto != IPPROTO_VRRP) { + log_info("vrid %d :: Invalid pkt - ip proto not valid %d", + vrrp->vrid, vnet->__pkt.header.proto); + return INVALID; + } + + /* verify VRRP version */ + if ((vrrpkt->version_type >> 4) != vrrp->version) { + log_info + ("vrid %d :: Invalid pkt - version %d mismatch, expecting %d", + vrrp->vrid, vrrpkt->version_type >> 4, vrrp->version); + return INVALID; + } + + /* TTL must be 255 */ + if (vnet->__pkt.header.ttl != VRRP_TTL) { + log_info("vrid %d :: Invalid pkt - TTL isn't %d", vrrp->vrid, + VRRP_TTL); + return INVALID; + } + + /* verify VRRP checksum */ + int chksum = vrrpkt->chksum; /* save checksum */ + if (vnet->adv_checksum(vnet, vrrpkt, &vnet->__pkt.s_ipx, + &vnet->__pkt.d_ipx) != chksum) { + log_info("vrid %d :: Invalid pkt - Invalid checksum %x", + vrrp->vrid, chksum); + + return INVALID; + } + /* restore checksum */ + vrrpkt->chksum = chksum; + + /* check if VRID is the same as the current instance */ + if (vrrpkt->vrid != vrrp->vrid) { + log_info("vrid %d :: Invalid pkt - Invalid VRID %d", + vrrp->vrid, vrrpkt->vrid); + return INVALID; + } + + /* local router is the IP address owner + * (Priority equals 255) + */ + if (vrrp->priority == PRIO_OWNER) { + log_info + ("vrid %d :: Invalid pkt - *We* are the owner of IP address(es) (priority %d)", + vrrp->vrid, vrrp->priority); + return INVALID; + } + + + /* Auth type (RFC2338/3768) */ + if (vrrp->version != RFC5798) { + + /* auth type must be the same locally configured */ + if (vrrpkt->auth_type != vrrp->auth_type) { + log_info + ("vrid %d :: Invalid pkt - Invalid authentication type", + vrrp->vrid); + return INVALID; + } + + /* auth type is simple */ + if (vrrpkt->auth_type == SIMPLE) { + uint32_t *auth_data = + (uint32_t *) ((unsigned char *) vrrpkt + + VRRP_PKTHDR_SIZE + + vrrp->naddr * sizeof(uint32_t)); + if (memcmp + (auth_data, vrrp->auth_data, + strlen(vrrp->auth_data)) + != 0) { + log_info + ("vrid %d :: Invalid pkt - Invalid authentication password", + vrrp->vrid); + return INVALID; + } + } + } + + /* count of IP address(es) and list may be the same + * or generated by the owner + */ + if (((vrrpkt->naddr != vrrp->naddr) + || (vnet->vip_compare(vnet, vrrpkt) != 0)) + && (vrrpkt->priority != PRIO_OWNER) && (vrrp->version == 2)) { + log_info + ("vrid %d :: Invalid pkt not generated by the owner, drop it", + vrrp->vrid); + return INVALID; + } + + /* advert interval must be the same as the locally configured */ + if (vrrpkt->adv_int != vrrp->adv_int) { + log_info + ("vrid %d :: Invalid pkt - Advertisement interval mismatch\n", + vrrp->vrid); + return INVALID; + } + + /* pkt is valid, keep it in internal buffer */ + memcpy(&vnet->__pkt.adv, vrrpkt, sizeof(struct vrrphdr)); + + return len; +} + +/** + * vrrp_net_send - send pkt + */ +int vrrp_net_send(const struct vrrp_net *vnet, struct iovec *iov, size_t len) +{ + if (iov == NULL) { + log_error("vrid %d :: No data to send !?", vnet->vrid); + return -1; + } + + struct sockaddr_ll device = { 0 }; + + device.sll_family = AF_PACKET; + device.sll_ifindex = if_nametoindex(vnet->vif.ifname); + + if (device.sll_ifindex == 0) { + log_error("vrid %d :: if_nametoindex - %s", vnet->vrid, + strerror(errno)); + return -1; + } + + struct msghdr msg = { 0 }; + + msg.msg_name = &device; + msg.msg_namelen = sizeof(device); + msg.msg_iov = iov; + msg.msg_iovlen = len; + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_flags = 0; + + if (sendmsg(vnet->xmit, &msg, 0) < 0) { + log_error("vrid %d :: sendmsg - %s", vnet->vrid, + strerror(errno)); + return -1; + } + + return 0; +} diff --git a/vrrp_net.h b/vrrp_net.h new file mode 100644 index 0000000..db266d8 --- /dev/null +++ b/vrrp_net.h @@ -0,0 +1,148 @@ +/* + * vrrp_net.h + * + * Copyright (C) 2014 Arnaud Andre + * + * This file is part of uvrrpd. + * + * uvrrpd 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 3 of the License, or + * (at your option) any later version. + * + * uvrrpd is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with uvrrpd. If not, see . + */ + + +#ifndef _VRRP_NET_ +#define _VRRP_NET_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "vrrp_ipx.h" +#include "vrrp_rfc.h" +#include "list.h" + +/* from vrrp.h */ +struct vrrp; + +/** + * constants + */ +#define IPPROTO_VRRP 112 +#define VIP_MAX 255 + +#define VRRP_AUTH_SIZE 2*sizeof(uint32_t) +#define VRRP_VIPMAX_SIZE VIP_MAX * sizeof(uint32_t) +#define VRRP_PKTHDR_SIZE sizeof(struct vrrphdr) +#define VRRP_PKT_MINSIZE VRRP_PKTHDR_SIZE + sizeof(uint32_t) +#define VRRP_PKT_MAXSIZE VRRP_PKTHDR_SIZE + VRRP_VIPMAX_SIZE + VRRP_AUTH_SIZE + +#define IPHDR_SIZE sizeof(struct iphdr) + +/** + * struct vrrp_ip - VRRP IPs addresses + */ +struct vrrp_ip { + union vrrp_ipx_addr ipx; + uint8_t netmask; + struct list_head iplist; + /* internal buffer for topology update pkt */ + struct iovec __topology[3]; +}; + +/** + * struct vrrp_if - VRRP interface + */ +struct vrrp_if { + char *ifname; + int mtu; + union vrrp_ipx_addr ipx; +}; + + +/** + * struct vrrp_recv - VRRP buffer recv + */ +struct vrrp_recv { + union vrrp_ipx_addr s_ipx; + union vrrp_ipx_addr d_ipx; + struct vrrp_ipx_header header; + struct vrrphdr adv; +}; + +/** + * struct vrrp_net - VRRP net structure + */ +struct vrrp_net { + /* VRRP id */ + uint8_t vrid; + + /* VRRP interface */ + struct vrrp_if vif; + + /* list of VRRP IP adresses */ + struct list_head vip_list; + + /* family */ + int family; + + /* count IP addresses */ + uint8_t naddr; + + /* listen VRRP socket */ + int socket; + + /* xmit VRRP socket */ + int xmit; + + /* buffer for received pkt */ + struct vrrp_recv __pkt; + + /* buffer for advertisement pkt */ + struct iovec __adv[3]; + + /* family helper functions */ + struct vrrp_ipx *ipx_helper; +}; + + +/** + * enum vrrp_ret - Return code used in vrrp_net_listen + */ +enum vrrp_ret { + INVALID = -1, + PKT, /* valid packet received */ + SIGNAL, /* signal catch */ + TIMER /* timer expired */ +}; + +/* + * funcs + */ +void vrrp_net_init(struct vrrp_net *vnet); +void vrrp_net_cleanup(struct vrrp_net *vnet); +int vrrp_net_socket(struct vrrp_net *vnet); +int vrrp_net_socket_xmit(struct vrrp_net *vnet); +int vrrp_net_vif_getaddr(struct vrrp_net *vnet); +int vrrp_net_vif_mtu(struct vrrp_net *vnet); +int vrrp_net_vip_set(struct vrrp_net *vnet, const char *ip); +int vrrp_net_listen(struct vrrp_net *vnet, struct vrrp *vrrp); +int vrrp_net_recv(struct vrrp_net *vnet, const struct vrrp *vrrp); +int vrrp_net_send(const struct vrrp_net *vnet, struct iovec *iov, size_t len); + +#endif /* _VRRP_NET_ */ diff --git a/vrrp_options.c b/vrrp_options.c new file mode 100644 index 0000000..ba49451 --- /dev/null +++ b/vrrp_options.c @@ -0,0 +1,250 @@ +/* + * vrrp_options.c + * + * Copyright (C) 2014 Arnaud Andre + * + * This file is part of uvrrpd. + * + * uvrrpd 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 3 of the License, or + * (at your option) any later version. + * + * uvrrpd is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with uvrrpd. If not, see . + */ + +#include +#include +#include +#include + +#include "vrrp.h" +#include "vrrp_net.h" +#include "vrrp_options.h" +#include "common.h" +#include "log.h" + +/* from uvrrpd.c */ +extern int background; +extern char *loglevel; + +/** + * vrrp_usage() + */ +static void vrrp_usage(void) +{ + fprintf(stdout, + "Usage: uvrrpd -v vrid -i ifname [OPTIONS] VIP1 [… VIPn]\n\n" + "Mandatory options:\n" + " -v, --vrid vrid Virtual router identifier\n" + " -i, --interface iface Interface\n" + " VIP Virtual IP(s), 1 to 255 VIPs\n\n" + "Optional arguments:\n" + " -p, --priority prio Priority of VRRP Instance, (0-255, default 100)\n" + " -t, --time delay Time interval between advertisements\n" + " Seconds in VRRPv2 (default 1s),\n" + " Centiseconds in VRRPv3 (default 100cs)\n" + " -P, --preempt on|off Switch preempt (default on)\n" + " -r, --rfc version Specify protocol 'version'\n" + " 2 (VRRPv2, RFC3768) by default,\n" + " 3 (VRRPv3, RFC5798)\n" + " -6, --ipv6 IPv6 support, (only in VRRPv3)\n" + " -a, --auth pass Simple text password (only in VRRPv2)\n" + " -f, --foreground Execute uvrrpd in foreground\n" + " -s, --script Path of hook script (default /etc/uvrrpd/uvrrpd-switch.sh)\n" +#if 0 /* todo */ + " --pidfile name Create pid file 'name'\n" +#endif + " -d, --debug\n" " -h, --help\n"); +} + +/** + * vrrp_options() - Parse command line options + */ +int vrrp_options(struct vrrp *vrrp, struct vrrp_net *vnet, int argc, + char *argv[]) +{ + int optc; + unsigned long opt; /* strtoul */ + + static struct option const opts[] = { + {"vrid", required_argument, 0, 'v'}, + {"interface", required_argument, 0, 'i'}, + {"priority", required_argument, 0, 'p'}, + {"time", required_argument, 0, 't'}, + {"preempt", required_argument, 0, 'P'}, + {"rfc", required_argument, 0, 'r'}, + {"ipv6", no_argument, 0, '6'}, + {"auth", required_argument, 0, 'a'}, + {"foreground", no_argument, 0, 'f'}, + {"script", required_argument, 0, 's'}, + {"debug", no_argument, 0, 'd'}, + {"help", no_argument, 0, 'h'}, + {NULL, 0, 0, 0} + }; + + while ((optc = + getopt_long(argc, argv, "v:i:p:t:P:r:6a:fs:dh", opts, + NULL)) != EOF) { + switch (optc) { + + /* vrid */ + case 'v': + if ((mystrtoul(&opt, optarg, VRID_MAX) == -ERANGE) + || (opt == 0)) { + fprintf(stderr, "1 < vrid < 255\n"); + vrrp_usage(); + return -1; + } + vrrp->vrid = vnet->vrid = (uint8_t) opt; + break; + + /* interface */ + case 'i': + vnet->vif.ifname = strndup(optarg, IFNAMSIZ); + if (vnet->vif.ifname == NULL) { + perror("strndup"); + return -1; + } + break; + + /* priority */ + case 'p': + if (mystrtoul(&opt, optarg, VRRP_PRIO_MAX) == -ERANGE) { + fprintf(stderr, "0 < priority < 255\n"); + vrrp_usage(); + return -1; + } + vrrp->priority = (uint8_t) opt; + break; + + /* delay */ + case 't': + if (mystrtoul(&opt, optarg, ADVINT_MAX) == -ERANGE) { + vrrp_usage(); + return -1; + } + vrrp->adv_int = (uint16_t) opt; + break; + + /* preempt mode */ + case 'P': + if (matches(optarg, "on")) + vrrp->preempt = TRUE; + else if (matches(optarg, "off")) + vrrp->preempt = FALSE; + else { + fprintf(stderr, + "preempt mode 'on' or 'off', by default 'on'\n"); + vrrp_usage(); + return -1; + } + break; + + /* RFC - version */ + case 'r': + if ((mystrtoul(&opt, optarg, RFC5798) == -ERANGE) + || (opt < RFC3768)) { + fprintf(stderr, "Version 2 or 3 : %ld\n", opt); + vrrp_usage(); + return -1; + } + vrrp->version = (uint8_t) opt; + break; + + /* IPv6 */ + case '6': /* Force RFC5798/VRRPv3 */ + vrrp->version = RFC5798; + vnet->family = AF_INET6; + break; + + /* auth */ + case 'a': /* only SIMPLE password supported */ + if (strlen(optarg) > VRRP_AUTH_PASS_LEN) { + fprintf(stderr, + "Password too long (8 char max)\n"); + vrrp_usage(); + return -1; + } + vrrp->auth_data = strndup(optarg, VRRP_AUTH_PASS_LEN); + vrrp->auth_type = SIMPLE; + /* hide passwd from ps */ + strncpy(optarg, "********", strlen(optarg)); + break; + + /* foreground */ + case 'f': + background = 0; + break; + + + /* script */ + case 's': + vrrp->scriptname = strndup(optarg, VRRP_SCRIPT_MAX); + if (vrrp->scriptname == NULL) { + perror("strndup"); + return -1; + } + break; + + /* debug */ + case 'd': + loglevel = strndup("debug", 6); + break; + + /* help */ + case 'h': + default: + vrrp_usage(); + return -1; + break; + } + } + + /* Fetch virtual IP addresses */ + if (optind == argc) { + fprintf(stderr, "Specify at least one virtual IP addr !\n"); + vrrp_usage(); + return -1; + } + + /* Number of IP addresses */ + vrrp->naddr = vnet->naddr = argc - optind; + + /* Register vrrp_vip addresses */ + while (optind != argc) { + if (vrrp_net_vip_set(vnet, argv[optind]) != 0) { + fprintf(stderr, "Invalid IP\n"); + vrrp_usage(); + return -1; + } + ++optind; + } + + /* minimal configuration */ + if (vrrp->vrid == 0) { + fprintf(stderr, "Specify VRRP id\n"); + vrrp_usage(); + return -1; + } + if (vnet->vif.ifname == NULL) { + fprintf(stderr, "Specify VRRP interface\n"); + vrrp_usage(); + return -1; + } + + /* default adv int */ + if ((vrrp->version == RFC3768) && (vrrp->adv_int == 0)) + vrrp->adv_int = 1; + if ((vrrp->version == RFC5798) && (vrrp->adv_int == 0)) + vrrp->adv_int = 100; + + /* Get IP addresse from interface name */ + return vrrp_net_vif_getaddr(vnet); +} diff --git a/vrrp_options.h b/vrrp_options.h new file mode 100644 index 0000000..765be1e --- /dev/null +++ b/vrrp_options.h @@ -0,0 +1,28 @@ +/* + * vrrp_options.h + * + * Copyright (C) 2014 Arnaud Andre + * + * This file is part of uvrrpd. + * + * uvrrpd 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 3 of the License, or + * (at your option) any later version. + * + * uvrrpd is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with uvrrpd. If not, see . + */ + +#ifndef _VRRP_OPTIONS_H_ +#define _VRRP_OPTIONS_H_ + +int vrrp_options(struct vrrp *vrrp, struct vrrp_net *vnet, int argc, + char *argv[]); + +#endif /* _VRRP_OPTIONS_H_ */ diff --git a/vrrp_rfc.h b/vrrp_rfc.h new file mode 100644 index 0000000..e09e24b --- /dev/null +++ b/vrrp_rfc.h @@ -0,0 +1,54 @@ +/* + * vrrp_rfc.h + * + * Copyright (C) 2014 Arnaud Andre + * + * This file is part of uvrrpd. + * + * uvrrpd 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 3 of the License, or + * (at your option) any later version. + * + * uvrrpd is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with uvrrpd. If not, see . + */ + +#ifndef _VRRP_RFC_H_ +#define _VRRP_RFC_H_ + +#include + +/* + * vrrphdr + * Header structure for rfc3768 et rfc5798 + */ + +struct vrrphdr { + uint8_t version_type; /* 0-3=version, 4-7=type */ + uint8_t vrid; + uint8_t priority; + uint8_t naddr; + union { + + /* rfc 3768 */ + struct { + uint8_t auth_type; + uint8_t adv_int; + }; + + /* rfc 5798 */ + uint16_t max_adv_int; /* 0-3=rsvd, 4-5=adv_int */ + }; + uint16_t chksum; + + /* virtual IPs start here */ + +} __attribute__ ((packed)); + +#endif /* _VRRP_RFC_H_ */ diff --git a/vrrp_state.c b/vrrp_state.c new file mode 100644 index 0000000..0eab552 --- /dev/null +++ b/vrrp_state.c @@ -0,0 +1,320 @@ +/* + * vrrp_state.c + * + * Copyright (C) 2014 Arnaud Andre + * + * This file is part of uvrrpd. + * + * uvrrpd 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 3 of the License, or + * (at your option) any later version. + * + * uvrrpd is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with uvrrpd. If not, see . + */ + +#include + +#include "vrrp.h" +#include "vrrp_net.h" +#include "vrrp_adv.h" +#include "vrrp_arp.h" +#include "vrrp_na.h" +#include "vrrp_exec.h" + +#include "log.h" +#include "bits.h" +#include "uvrrpd.h" + +extern unsigned long reg; + +/** + * switching state functions + */ +static int vrrp_state_goto_master(struct vrrp *vrrp, struct vrrp_net *vnet); +static int vrrp_state_goto_backup(struct vrrp *vrrp, struct vrrp_net *vnet); + +/** + * vrrp_state_init() - Initial state of VRRP instance + * + * Switch to master or backup state + */ +int vrrp_state_init(struct vrrp *vrrp, struct vrrp_net *vnet) +{ + log_notice("vrid %d :: %s", vrrp->vrid, "init"); + + /* init Master_Adver_Interval */ + vrrp->master_adv_int = vrrp->adv_int; + log_debug("%d", vrrp->master_adv_int); + + /* router owns ip address(es) */ + if (vrrp->priority == 255) { + log_debug("%s %d :%s", "priority", vrrp->priority, + "router owns VIP address(es)"); + return vrrp_state_goto_master(vrrp, vnet); + } + + return vrrp_state_goto_backup(vrrp, vnet); +} + +/** + * vrrp_state_backup() - handle backup state + */ +int vrrp_state_backup(struct vrrp *vrrp, struct vrrp_net *vnet) +{ + int event = vrrp_net_listen(vnet, vrrp); + + switch (event) { + case TIMER: + log_notice("vrid %d :: %s", vrrp->vrid, + "masterdown_timer expired"); + + vrrp_state_goto_master(vrrp, vnet); + break; + + case PKT: /* valid PKT adv */ + /* Must be impossible */ + if (vrrp_adv_get_version(vnet) == 0) { + log_error("vrid %d :: %s", vrrp->vrid, + "recv buffer empty !?"); + break; + } + + log_debug("vrid %d :: %s", vrrp->vrid, + "pkt received from current master"); + log_debug("vrid %d :: %s:%d %s:%d %s:%s", vrrp->vrid, "prio", + vrrp->priority, "prio recv", + vrrp_adv_get_priority(vnet), "preempt", + STR_PREEMPT(vrrp->preempt)); + + /* Priority of received pkt is 0 + * => set Master_Down_Timer to skew_time + */ + if (vrrp_adv_get_priority(vnet) == 0) { + log_info("vrid %d :: %s", vrrp->vrid, + "receive packet with priority 0"); + log_notice("vrid %d :: %s %d", vrrp->vrid, + "set masterdown_timer to skew_time", + SKEW_TIME(vrrp)); + + VRRP_SET_SKEW_TIME(vrrp); + break; + } + + /* Master has send its adv pkt, or preemption mode is false + */ + if ((vrrp->preempt == FALSE) || + (vrrp_adv_get_priority(vnet) >= vrrp->priority)) { + + /* RFC5798: Set Master_Adver_Interval to + * Advertisement_Interval + */ + if (vrrp->version == RFC5798) + vrrp->master_adv_int = + vrrp_adv_get_advint(vnet); + +#ifdef DEBUG + print_buf_hexa("hexa master_adv_int", + &vrrp->master_adv_int, sizeof(uint16_t)); +#endif + + + + VRRP_SET_MASTERDOWN_TIMER(vrrp); + break; + } + + /* Our priority is greater and preemption mode is true + * So we discard advertisement. + * Maybe we'll become Master if Master_Down_Timer expire, + */ + log_info("vrid %d :: %s", vrrp->vrid, "discard advertisement"); + + break; + + case SIGNAL: + log_debug("vrid %d :: signal", vrrp->vrid); + + /* shutdown/reload event ? */ + if (test_and_clear_bit(UVRRPD_RELOAD, ®)) { + vrrp_timer_clear(&vrrp->masterdown_timer); + vrrp->state = INIT; + } + + break; + + case INVALID: + log_warning("vrid %d :: %s %s, %s", vrrp->vrid, + "receive an invalid advertisement packet from", + vrrp_adv_get_ntoa_addr(vnet), "ignore it"); + + break; + + default: + log_error("vrid %d :: %s r:%d", vrrp->vrid, "unknown event", + event); + break; + } + + return event; +} + + +/** + * vrrp_state_master() - handle master state + */ +int vrrp_state_master(struct vrrp *vrrp, struct vrrp_net *vnet) +{ + int event = vrrp_net_listen(vnet, vrrp); + + switch (event) { + case TIMER: + /* adv_timer expired, time to send another */ + log_info("vrid %d :: %s", vrrp->vrid, "adv_timer expired"); + vrrp_adv_send(vnet); + VRRP_SET_ADV_TIMER(vrrp); + break; + + case PKT: + if (vrrp_adv_get_version(vnet) == 0) { + log_error("vrid %d :: %s", vrrp->vrid, + "recv buffer empty !?"); + break; + } + + log_debug("vrid %d :: %s:%d %s:%d %s:%s", vrrp->vrid, "prio", + vrrp->priority, "prio recv", + vrrp_adv_get_priority(vnet), "preempt", + STR_PREEMPT(vrrp->preempt)); + + /* Priority of received pkt is 0 + * We send an advertisement + * and rearm Adv_timer + */ + if (vrrp_adv_get_priority(vnet) == 0) { + log_info("vrid %d :: %s", vrrp->vrid, + "receive packet with priority 0"); + vrrp_adv_send(vnet); + VRRP_SET_ADV_TIMER(vrrp); + break; + } + + /* Priority of received pkt is greater than our, + * or Priority of received pkt is equal, but + * primary IP address of the sender is greater than + * the local primary IP address + * => switch to backup + * TODO : /!\ don't work in IPv6 /!\ + */ + if ((vrrp_adv_get_priority(vnet) > vrrp->priority) || + ((vrrp_adv_get_priority(vnet) == vrrp->priority) && + (vrrp_adv_get_ntohl_addr(vnet) > + ntohl(vnet->vif.ip_addr.s_addr)))) { + + log_notice("vrid %d :: %s", vrrp->vrid, + "receive packet with higher priority"); + + vrrp_state_goto_backup(vrrp, vnet); + break; + } + + /* We have the greatest priority */ + log_info("vrid %d :: %s", vrrp->vrid, "discard advertisement"); + + break; + + case SIGNAL: + log_debug("vrid %d :: signal", vrrp->vrid); + + /* shutdown/reload event ? */ + if (test_and_clear_bit(UVRRPD_RELOAD, ®)) { + vrrp_timer_clear(&vrrp->adv_timer); + vrrp_adv_send_zero(vnet); + /* berk */ + vrrp_exec(vrrp, vnet, BACKUP); + vrrp->state = INIT; + } + + break; + + case INVALID: + log_warning("vrid %d :: invalid event", vrrp->vrid); + break; + + default: + log_error("vrid %d :: %s r:%d", vrrp->vrid, "unknown event", + event); + break; + } + + return event; +} + + +/** + * vrrp_state_goto_master() - switch state to master + */ +static int vrrp_state_goto_master(struct vrrp *vrrp, struct vrrp_net *vnet) +{ + log_notice("vrid %d :: %s -> %s", vrrp->vrid, + STR_STATE(vrrp->state), "master"); + + vrrp->state = MASTER; + + /* IPv4 specific */ + vrrp_adv_send(vnet); + + if (vnet->family == AF_INET) + vrrp_arp_send(vnet); + else if (vnet->family == AF_INET6) + vrrp_na_send(vnet); + + /* script */ + vrrp_exec(vrrp, vnet, vrrp->state); + + /* reset masterdown_timer && set ADV timer */ + vrrp_timer_clear(&vrrp->masterdown_timer); + VRRP_SET_ADV_TIMER(vrrp); + + return 0; +} + + +/** + * vrrp_state_goto_backup() - switch state to backup + */ +static int vrrp_state_goto_backup(struct vrrp *vrrp, struct vrrp_net *vnet) +{ + log_notice("vrid %d :: %s -> %s", vrrp->vrid, + STR_STATE(vrrp->state), "backup"); + + int previous_state = vrrp->state; + vrrp->state = BACKUP; + + log_debug("%s:%s", STR_STATE(previous_state), STR_STATE(vrrp->state)); + + /* script */ + if (previous_state != INIT) + vrrp_exec(vrrp, vnet, vrrp->state); + + if (vrrp->version == RFC5798) { + if (previous_state == INIT) + vrrp->master_adv_int = vrrp->adv_int; + if (previous_state == MASTER) + vrrp->master_adv_int = vrrp_adv_get_advint(vnet); + } + + /* clear adv timer && set masterdown_timer */ + vrrp_timer_clear(&vrrp->adv_timer); + VRRP_SET_MASTERDOWN_TIMER(vrrp); + + log_debug("%d %d", vrrp->master_adv_int, + 3 * vrrp->master_adv_int + SKEW_TIME(vrrp)); + return 0; +} diff --git a/vrrp_state.h b/vrrp_state.h new file mode 100644 index 0000000..96055e0 --- /dev/null +++ b/vrrp_state.h @@ -0,0 +1,42 @@ +/* + * vrrp_state.h - VRRP state machine + * + * Copyright (C) 2014 Arnaud Andre + * + * This file is part of uvrrpd. + * + * uvrrpd 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 3 of the License, or + * (at your option) any later version. + * + * uvrrpd is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with uvrrpd. If not, see . + */ + +#ifndef _VRRP_STATE_H_ +#define _VRRP_STATE_H_ + +/** + * vrrp_state - VRRP states + */ +typedef enum { + INIT, + BACKUP, + MASTER +} vrrp_state; + + +#define STR_STATE(s) (s == INIT ? "init" : \ + ((s == BACKUP) ? "backup" : "master")) + +int vrrp_state_init(struct vrrp *vrrp, struct vrrp_net *vnet); +int vrrp_state_master(struct vrrp *vrrp, struct vrrp_net *vnet); +int vrrp_state_backup(struct vrrp *vrrp, struct vrrp_net *vnet); + +#endif /* _VRRP_STATE_H_ */ diff --git a/vrrp_switch.sh b/vrrp_switch.sh new file mode 100755 index 0000000..61a500c --- /dev/null +++ b/vrrp_switch.sh @@ -0,0 +1,62 @@ +#!/bin/sh + +state=$1 +vrid=$2 +ifname=$3 +priority=$4 +adv_int=$5 +naddr=$6 +family=$7 +ips=$8 + +echo "state\t\t$state +vrid\t\t$vrid +ifname\t\t$ifname +priority\t$priority +adv_int\t\t$adv_int +naddr\t\t$naddr +ips\t\t$ips" > /tmp/state.vrrp_${vrid}_${ifname} + +interface=vrrp_${ifname}_${vrid} + +echo $ips + +case "$state" in + + "init" ) + # adjust sysctl + sysctl -w net.ipv4.conf.all.rp_filter=0 + sysctl -w net.ipv4.conf.all.arp_ignore=1 + sysctl -w net.ipv4.conf.all.arp_announce=2 + + ;; + + "master" ) + # create macvlan interface + HEXA_VRID=$(printf %x $vrid) + ip link add link $ifname address 00:00:5E:00:01:$HEXA_VRID $interface type macvlan + + # set virtual ips addresses + OIFS=$IFS + IFS=',' + + for ip in $ips; do + ip -$family addr add $ip dev $interface + sysctl -w net.ipv6.conf.$interface.autoconf=0 + sysctl -w net.ipv6.conf.$interface.accept_ra=0 + sysctl -w net.ipv6.conf.$interface.forwarding=1 + done + ip link set dev $interface up + + IFS=$OIFS + + ;; + + "backup" ) + # destroy macvlan interface + ip link del $interface + + ;; +esac + +exit 0 diff --git a/vrrp_timer.c b/vrrp_timer.c new file mode 100644 index 0000000..97c0d90 --- /dev/null +++ b/vrrp_timer.c @@ -0,0 +1,168 @@ +/* + * vrrp_timer.c - functions manipulating VRRP timers + * + * Copyright (C) 2014 Arnaud Andre + * + * This file is part of uvrrpd. + * + * uvrrpd 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 3 of the License, or + * (at your option) any later version. + * + * uvrrpd is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with uvrrpd. If not, see . + * + * + * These functions use clock_gettime() and CLOCK_MONOTONIC_RAW + * which access to a raw hardware-based time that is not subject to + * NTP adjustements. + */ + +#include +#include +#include +#include + +#include "vrrp_timer.h" +#include "log.h" + +/* 1s for timespec operations */ +#define NANOUL 1000000000 +#define CENTUL 10000000 + +/** + * timespec_substract() - substract two timestamp + * + * Subtract the `struct timespec' values X and Y, + * The difference is stored in timespect result. + * @return 1 if the difference is negative, otherwise 0. + */ +static inline int timespec_substract(struct timespec *result, + struct timespec *x, struct timespec *y) +{ + /* Perform the carry for the later subtraction by updating y. */ + if (x->tv_nsec < y->tv_nsec) { + time_t nsec = (y->tv_nsec - x->tv_nsec) / NANOUL + 1; + y->tv_nsec -= NANOUL * nsec; + y->tv_sec += nsec; + } + if (x->tv_nsec - y->tv_nsec > NANOUL) { + time_t nsec = (x->tv_nsec - y->tv_nsec) / NANOUL; + y->tv_nsec += NANOUL * nsec; + y->tv_sec -= nsec; + } + + /* Compute the time remaining to wait. + tv_nsec is certainly positive. */ + result->tv_sec = x->tv_sec - y->tv_sec; + result->tv_nsec = x->tv_nsec - y->tv_nsec; + + /* Return 1 if result is negative. */ + return x->tv_sec < y->tv_sec; +} + +/** + * vrrp_timer_set() - set timer and reset delta + * + * @delay + * @return -1 if clock_gettime() fail, 0 else + */ +int vrrp_timer_set(struct vrrp_timer *timer, time_t delay, long delay_cs) +{ + if (clock_gettime(CLOCK_MONOTONIC_RAW, &timer->ts) == -1) { + log_error("clock_gettime: %s", strerror(errno)); + return -1; + } + + timer->ts.tv_sec += delay; + timer->ts.tv_nsec += delay_cs * CENTUL; + + log_debug("delay %ld", delay); + log_debug("delay_cs %ld", delay_cs); + + log_debug("timer->ts.tv_sec %ld", timer->ts.tv_sec); + log_debug("timer->ts.tv_nsec %ld", timer->ts.tv_nsec); + + /* reset delta */ + timer->delta.tv_sec = 0; + timer->delta.tv_nsec = 0; + + return 0; +} + +/** + * vrrp_timer_clear() - clear (reset) timer + */ +void vrrp_timer_clear(struct vrrp_timer *timer) +{ + if (timer == NULL) + return; + + timer->ts.tv_sec = 0; + timer->ts.tv_nsec = 0; + timer->delta.tv_sec = 0; + timer->delta.tv_nsec = 0; +} + +/** + * vrrp_timer_is_running() - determine if a timer is + * currently used + * + * @return 1 if running, 0 else + */ +int vrrp_timer_is_running(struct vrrp_timer *timer) +{ + if ((timer->ts.tv_sec != 0) || (timer->ts.tv_nsec != 0)) + return 1; + + return 0; +} + +/** + * vrrp_timer_update() - update a timer + * + * @return -1 if clock_gettime() failed + * @return ETIME if timer is expired + * @return 0 if timer is successfully updated + */ +int vrrp_timer_update(struct vrrp_timer *timer) +{ + struct timespec ts; + + if (clock_gettime(CLOCK_MONOTONIC_RAW, &ts) == -1) { + log_error("clock_gettime: %s", strerror(errno)); + return -1; /* TODO die() */ + } + + if (timespec_substract(&timer->delta, &timer->ts, &ts)) { + log_debug("current timer expired"); + return 1; + } + + timer->ts.tv_sec = ts.tv_sec + timer->delta.tv_sec; + timer->ts.tv_nsec = ts.tv_nsec + timer->delta.tv_nsec; + + log_debug("timer->ts.tv_sec %ld", timer->ts.tv_sec); + log_debug("timer->ts.tv_nsec %ld", timer->ts.tv_nsec); + + return 0; +} + +/** + * vrrp_timer_is_expired() - check if a timer is expired + * + */ +int vrrp_timer_is_expired(struct vrrp_timer *timer) +{ + if (vrrp_timer_update(timer) + || ((timer->ts.tv_sec <= 0) && (timer->ts.tv_nsec <= 0))) + return 1; + + return 0; +} diff --git a/vrrp_timer.h b/vrrp_timer.h new file mode 100644 index 0000000..cf4b290 --- /dev/null +++ b/vrrp_timer.h @@ -0,0 +1,75 @@ +/* + * vrrp_timer.h - functions manipulating VRRP timers + * + * These functions use clock_gettime() and CLOCK_MONOTONIC_RAW + * which use a raw hardware-based time that is not subject to + * NTP adjustements. + * + * Copyright (C) 2014 Arnaud Andre + * + * This file is part of uvrrpd. + * + * uvrrpd 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 3 of the License, or + * (at your option) any later version. + * + * uvrrpd is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with uvrrpd. If not, see . + */ + + +#ifndef _VRRP_TIMER_H_ +#define _VRRP_TIMER_H_ + +#include + +/** + * struct vrrp_timer + * + * @ts timestamp in ns + * @delta time since the last update + */ +struct vrrp_timer { + struct timespec ts; + struct timespec delta; +}; + +/* prototype functions */ +int vrrp_timer_set(struct vrrp_timer *timer, time_t delay, long delay_cs); +void vrrp_timer_clear(struct vrrp_timer *timer); +int vrrp_timer_is_running(struct vrrp_timer *timer); +int vrrp_timer_update(struct vrrp_timer *timer); +int vrrp_timer_is_expired(struct vrrp_timer *timer); + +/* Specific VRRP timer macros */ + +#define SKEW_TIME( v ) \ + ( (256 - v->priority) * \ + (v->version==3?v->master_adv_int:1) / 256 ) + +#define MASTERDOWN_INT( v ) \ + (3 * v->master_adv_int + SKEW_TIME( v )) + +#define VRRP_SET_ADV_TIMER( v ) \ + vrrp_timer_set(&v->adv_timer, \ + (v->version == 3 ? 0:v->adv_int), \ + (v->version == 3 ? v->master_adv_int:0)) + +#define VRRP_SET_MASTERDOWN_TIMER( v ) \ + vrrp_timer_set(&v->masterdown_timer, \ + (v->version == 3 ? 0:MASTERDOWN_INT( v )), \ + (v->version == 3 ? MASTERDOWN_INT( v ):0)) + +#define VRRP_SET_SKEW_TIME( v ) \ + vrrp_timer_set(&v->masterdown_timer, \ + (v->version == 3 ? 0:SKEW_TIME( v )), \ + (v->version == 3 ? SKEW_TIME( v ):0)) + + +#endif /* _VRRP_TIMER_H_ */