├── .gitignore ├── COPYING ├── Makefile.am ├── README.md ├── bits.h ├── common.h ├── configure.ac ├── list.h ├── log.c ├── log.h ├── uvrrpd.c ├── uvrrpd.h ├── vrrp.c ├── vrrp.h ├── vrrp_adv.c ├── vrrp_adv.h ├── vrrp_arp.c ├── vrrp_arp.h ├── vrrp_ctrl.c ├── vrrp_ctrl.h ├── vrrp_exec.c ├── vrrp_exec.h ├── vrrp_ip4.c ├── vrrp_ip6.c ├── vrrp_ipx.h ├── vrrp_na.c ├── vrrp_na.h ├── vrrp_net.c ├── vrrp_net.h ├── vrrp_options.c ├── vrrp_options.h ├── vrrp_rfc.h ├── vrrp_state.c ├── vrrp_state.h ├── vrrp_switch.sh ├── vrrp_timer.c └── vrrp_timer.h /.gitignore: -------------------------------------------------------------------------------- 1 | *.[oa] 2 | *.lo 3 | *~ 4 | *.swp 5 | .libs/ 6 | _libs/ 7 | .deps/ 8 | uvrrpd 9 | deploy.sh 10 | libtool 11 | Makefile.in 12 | Makefile 13 | aclocal.m4 14 | config/ 15 | config.log 16 | config.status 17 | configure 18 | autom4te.cache/ 19 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | 676 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | ACLOCAL_AMFLAGS = -I config 2 | 3 | sbin_SCRIPTS = vrrp_switch.sh 4 | EXTRA_DIST = vrrp_switch.sh 5 | 6 | AM_CPPFLAGS = $(DEBUG_OPTS) -DPATH="$(sbindir)" -DPATHRUN="$(localestatedir)/run" 7 | 8 | AM_CFLAGS = -Wall -W -Werror 9 | 10 | sbin_PROGRAMS = uvrrpd 11 | 12 | noinst_HEADERS = \ 13 | bits.h \ 14 | common.h \ 15 | list.h \ 16 | log.h \ 17 | uvrrpd.h \ 18 | vrrp_adv.h \ 19 | vrrp_arp.h \ 20 | vrrp_ctrl.h \ 21 | vrrp_exec.h \ 22 | vrrp.h \ 23 | vrrp_ipx.h \ 24 | vrrp_na.h \ 25 | vrrp_net.h \ 26 | vrrp_options.h \ 27 | vrrp_rfc.h \ 28 | vrrp_state.h \ 29 | vrrp_timer.h 30 | 31 | uvrrpd_SOURCES = \ 32 | log.c \ 33 | uvrrpd.c \ 34 | vrrp_adv.c \ 35 | vrrp_arp.c \ 36 | vrrp_ctrl.c \ 37 | vrrp.c \ 38 | vrrp_exec.c \ 39 | vrrp_ip4.c \ 40 | vrrp_ip6.c \ 41 | vrrp_na.c \ 42 | vrrp_net.c \ 43 | vrrp_options.c \ 44 | vrrp_state.c \ 45 | vrrp_timer.c 46 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # uvrrpd 2 | 3 | uvrrpd is a VRRP daemon written in C, providing a full implementation of 4 | VRRPv2 (rfc3768) and VRRPv3 (rfc5798), with IPv4 and IPv6 support. 5 | 6 | uvrrpd is a project hosted at [Evolix's forge](https://forge.evolix.org/projects/uvrrpd) 7 | 8 | uvrrpd is written for GNU/Linux and use macvlan in order to derive multiple 9 | virtual NICs (virtual VRRP mac) from a single physical NIC. 10 | 11 | uvrrpd is a simply VRRP state machine, and a script (*vrrp_switch.sh*) is in 12 | charge to create or destroy Virtual VRRP interfaces. 13 | 14 | uvrrpd is designed to run a single VRRP instance, but you can run multiple 15 | instances of uvrrpd, each of them with a different VRRP id, on the same or 16 | different physical NIC. 17 | 18 | Simple text authentication from deprecated RFC2332 may be used while running 19 | uvrrpd in version 2 (rfc3768), but not in version 3 (rfc5798). 20 | 21 | It provides a network topology update by sending : 22 | - an ARP gratuitous packet for each Virtual VRRP IPv4 address specified in the 23 | VRRP instance, 24 | - an NDP neighbour advertisement for each Virtual VRRP IPv6 address. 25 | 26 | ## Building 27 | 28 | uvrrpd uses the autotools, so to build it from the released tarball, follow the 29 | usual procedure. 30 | 31 | ``` 32 | ./configure 33 | make 34 | sudo make install 35 | ``` 36 | 37 | If building from the git sources, run: 38 | ``` 39 | autoreconf -i 40 | ``` 41 | before that. 42 | 43 | That's all. You need the binary `uvrrpd` and the shell script *vrrp_switch.sh* 44 | to start playing, they are installed in $prefix/sbin, the default prefix being 45 | /usr/local. 46 | 47 | ## Usage 48 | 49 | ``` 50 | $ ./uvrrpd -h 51 | Usage: uvrrpd -v vrid -i ifname [OPTIONS] VIP1 [… VIPn] 52 | 53 | Mandatory options: 54 | -v, --vrid vrid Virtual router identifier 55 | -i, --interface iface Interface 56 | VIP Virtual IP(s), 1 to 255 VIPs 57 | 58 | Optional arguments: 59 | -p, --priority prio Priority of VRRP Instance, (0-255, default 100) 60 | -t, --time delay Time interval between advertisements 61 | Seconds in VRRPv2 (default 1s), 62 | Centiseconds in VRRPv3 (default 100cs) 63 | -T, --start-delay delay Use custom delay in INIT state, override masterdown 64 | timer 65 | Seconds in VRRPv2 (default 0s), 66 | Centiseconds in VRRPv3 (default 0cs) 67 | -P, --preempt on|off Switch preempt (default on) 68 | -r, --rfc version Specify protocol 'version' 69 | 2 (VRRPv2, RFC3768) by default, 70 | 3 (VRRPv3, RFC5798) 71 | -6, --ipv6 IPv6 support, (only in VRRPv3) 72 | -a, --auth pass Simple text password (only in VRRPv2) 73 | -f, --foreground Execute uvrrpd in foreground 74 | -s, --script Path of hook script (default /usr/local/sbin/vrrp_switch.sh) 75 | -F --pidfile name Use alternate pid file 'name' 76 | Default /run/uvrrp_${vrid}.pid 77 | -C --control name Use alternate control file 'name' 78 | Default /run/uvrrpd_ctrl.${vrid} 79 | -d, --debug 80 | -h, --help 81 | ``` 82 | 83 | ### Signals 84 | 85 | * `SIGHUP` : force uvrrpd to switch to init state 86 | * `SIGUSR1`|`SIGUSR2` : dump VRRP instance informations 87 | 88 | ### Control fifo 89 | 90 | User can send command through a control FIFO, by default in /var/run/uvrrpd_ctrl.${vrid} 91 | 92 | Commands available: 93 | * reload (force init state) 94 | * stop (exit) 95 | * state || status (dump vrrp status) 96 | * prio X (change priority while running, and switch to init state) 97 | 98 | ``` 99 | # ./uvrrpd -v 42 -i eth0 10.0.0.254 100 | # echo "prio 90" > /var/run/uvrrpd_ctrl.42 101 | # 102 | # tail -10 /var/log/daemon.log 103 | […] 104 | uvrrpd[27820]: vrid 42 :: new prio 90 applied 105 | uvrrpd[27820]: vrid 42 :: init 106 | uvrrpd[27820]: vrid 42 :: init -> backup 107 | uvrrpd[27820]: vrid 42 :: masterdown_timer expired 108 | […] 109 | # 110 | ``` 111 | 112 | ### Log 113 | 114 | LOG_DAEMON facility 115 | 116 | *vrrp_switch.sh* maintain a state file of the current instance in /tmp/state.vrrp_${vrid}_${ifname} 117 | 118 | ## Examples 119 | 120 | **uvrrpd must be run as root.** 121 | 122 | * Start a VRRP instance on eth0 interface with VRID 42, default priority (100), 123 | with *vrrp_switch.sh* in */usr/local/sbin/* directory. 124 | 125 | ``` 126 | # ./uvrrpd -v 42 -i eth0 -s /usr/local/sbin/vrrp_switch.sh 10.0.0.254 127 | ``` 128 | 129 | In our example, no other VRRP instance, we are the master and we can see the 130 | new VRRP interface with the VIP *10.0.0.254* and the virtual VRRP mac address 131 | *00:00:5e:00:01:2a* : 132 | 133 | ``` 134 | # ifconfig 135 | eth0 Link encap:Ethernet HWaddr 52:54:00:4f:48:3f 136 | inet addr:10.0.0.1 Bcast:10.0.0.255 Mask:255.255.255.0 137 | inet6 addr: fe80::5054:ff:fe4f:483f/64 Scope:Link 138 | UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 139 | RX packets:4935 errors:0 dropped:0 overruns:0 frame:0 140 | TX packets:3835 errors:0 dropped:0 overruns:0 carrier:0 141 | collisions:0 txqueuelen:1000 142 | RX bytes:965166 (942.5 KiB) TX bytes:613308 (598.9 KiB) 143 | 144 | eth0_42 Link encap:Ethernet HWaddr 00:00:5e:00:01:2a 145 | inet addr:10.0.0.254 Bcast:0.0.0.0 Mask:255.255.255.255 146 | inet6 addr: fe80::200:5eff:fe00:12a/64 Scope:Link 147 | UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 148 | RX packets:12 errors:0 dropped:0 overruns:0 frame:0 149 | TX packets:6 errors:0 dropped:0 overruns:0 carrier:0 150 | collisions:0 txqueuelen:0 151 | RX bytes:3217 (3.1 KiB) TX bytes:520 (520.0 B) 152 | […] 153 | ``` 154 | 155 | See logs : 156 | 157 | ``` 158 | # tail -f /var/log/daemon.log 159 | […] 160 | uvrrpd[2966]: vrid 42 :: init 161 | uvrrpd[2966]: vrid 42 :: init -> backup 162 | uvrrpd[2966]: vrid 42 :: masterdown_timer expired 163 | uvrrpd[2966]: vrid 42 :: backup -> master 164 | ``` 165 | 166 | and /tmp/state.vrrp_42_eth0 : 167 | 168 | ``` 169 | # cat /tmp/state.vrrp_42_eth0 170 | state master 171 | vrid 42 172 | ifname eth0 173 | priority 100 174 | adv_int 1 175 | naddr 1 176 | ips 10.0.0.254 177 | ``` 178 | 179 | You can start an another VRRP instance on another GNU/Linux box or a router with VRRP support, with the same VRID and different priority. 180 | 181 | * uvrrpd support IPv6 (RFC5798) : 182 | 183 | ``` 184 | # ./uvrrpd -v 42 -i eth0 -p 90 -6 fe80::fada/64 185 | ``` 186 | 187 | * Multiple VIPs could be specified for a single VRRP instance (up to 255 VIPs) : 188 | 189 | ``` 190 | # ./uvrrpd -v 42 -i eth0 10.0.0.69 10.0.0.80 191 | ``` 192 | 193 | ## TODOs 194 | 195 | * more tests 196 | * packaging 197 | * ... 198 | 199 | Any suggestions, ideas, patches or whatever are welcome and will be greatly 200 | appreciated ! 201 | -------------------------------------------------------------------------------- /bits.h: -------------------------------------------------------------------------------- 1 | /* 2 | * bits.h 3 | * 4 | * Copyright (C) 2014 Arnaud Andre 5 | * 6 | * This file is part of uvrrpd. 7 | * 8 | * uvrrpd is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * uvrrpd is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with uvrrpd. If not, see . 20 | */ 21 | 22 | #ifndef _BITS_H_ 23 | #define _BITS_H_ 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | /** 33 | * BIT_MASK(nr) - bit mask 34 | */ 35 | #define BIT_MASK(nr) (1 << (nr)) 36 | 37 | 38 | /** 39 | * set_bit - Set a bit in memory 40 | */ 41 | static inline void set_bit(int nr, unsigned long *addr) 42 | { 43 | *addr |= BIT_MASK(nr); 44 | } 45 | 46 | /** 47 | * clear_bit - Clear a bit in memory 48 | */ 49 | static inline void clear_bit(int nr, unsigned long *addr) 50 | { 51 | *addr &= ~BIT_MASK(nr); 52 | } 53 | 54 | /** 55 | * change_bit - Toggle a bit in memory 56 | */ 57 | static inline void change_bit(int nr, unsigned long *addr) 58 | { 59 | *addr ^= BIT_MASK(nr); 60 | } 61 | 62 | /** 63 | * test_and_set_bit - Set a bit and return its old value 64 | */ 65 | static inline int test_and_set_bit(int nr, unsigned long *addr) 66 | { 67 | unsigned long mask = BIT_MASK(nr); 68 | unsigned long old = *addr; 69 | 70 | *addr = old | mask; 71 | return (old & mask) != 0; 72 | } 73 | 74 | /** 75 | * test_and_clear_bit - Clear a bit and return its old value 76 | */ 77 | static inline int test_and_clear_bit(int nr, unsigned long *addr) 78 | { 79 | unsigned long mask = BIT_MASK(nr); 80 | unsigned long old = *addr; 81 | 82 | *addr = old & ~mask; 83 | return (old & mask) != 0; 84 | } 85 | 86 | /** 87 | * test_and_change_bit - Toggle a bit and return its old value 88 | */ 89 | static inline int test_and_change_bit(int nr, unsigned long *addr) 90 | { 91 | unsigned long mask = BIT_MASK(nr); 92 | unsigned long old = *addr; 93 | 94 | *addr = old ^ mask; 95 | return (old & mask) != 0; 96 | } 97 | 98 | /** 99 | * test_bit - Determine whether a bit is set 100 | */ 101 | static inline int test_bit(int nr, const unsigned long *addr) 102 | { 103 | return 1UL & (*addr >> nr); 104 | } 105 | 106 | #endif /* _BITS_H_ */ 107 | -------------------------------------------------------------------------------- /common.h: -------------------------------------------------------------------------------- 1 | /* 2 | * common.h - common types and macros 3 | * 4 | * Copyright (C) 2014 Arnaud Andre 5 | * 6 | * This file is part of uvrrpd. 7 | * 8 | * uvrrpd is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * uvrrpd is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with uvrrpd. If not, see . 20 | */ 21 | 22 | 23 | #ifndef _COMMON_H_ 24 | #define _COMMON_H_ 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include "log.h" 36 | 37 | /** 38 | * bool - boolean type 39 | */ 40 | typedef enum { 41 | FALSE, 42 | TRUE 43 | } bool; 44 | 45 | /** 46 | * matches( s, c_str ) - Compare strings. 47 | * s Data strings 48 | * c_str C-Strings 49 | */ 50 | #define matches( s, c_str ) \ 51 | ({ \ 52 | const char __dummy[] = c_str; \ 53 | (void)(&__dummy); \ 54 | ( memcmp ( s, c_str, sizeof( c_str ) ) == 0 ); \ 55 | }) 56 | 57 | /** 58 | * stringify(x) 59 | */ 60 | #define _stringify(x) #x 61 | #define stringify(x) _stringify(x) 62 | 63 | /** 64 | * ARRAY_SIZE() 65 | */ 66 | #define ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0])) 67 | 68 | /** 69 | * max(a,b) 70 | */ 71 | #define max(a,b) \ 72 | ({ \ 73 | __typeof__ (a) _a = (a); \ 74 | __typeof__ (b) _b = (b); \ 75 | _a > _b ? _a : _b; \ 76 | }) 77 | 78 | /** 79 | * WHITESPACE 80 | */ 81 | #define WHITESPACE " \f\r\t\n\v" 82 | 83 | /** 84 | * cksum - compute IP checksum 85 | */ 86 | static inline int unsigned short cksum(unsigned short *buf, int nbytes) 87 | { 88 | uint32_t sum; 89 | uint16_t oddbyte; 90 | 91 | sum = 0; 92 | while (nbytes > 1) { 93 | sum += *buf++; 94 | nbytes -= 2; 95 | } 96 | 97 | if (nbytes == 1) { 98 | oddbyte = 0; 99 | *((uint16_t *) & oddbyte) = *(uint16_t *) buf; 100 | sum += oddbyte; 101 | } 102 | 103 | sum = (sum >> 16) + (sum & 0xffff); 104 | sum += (sum >> 16); 105 | 106 | return (uint16_t) ~ sum; 107 | } 108 | 109 | /** 110 | * mystrtoul - convert a string to an unsigned long int 111 | */ 112 | static inline int mystrtoul(unsigned long *const dest, 113 | const char *const str, unsigned long max) 114 | { 115 | unsigned long val; 116 | char *endptr; 117 | 118 | errno = 0; 119 | val = strtoull(str, &endptr, 0); 120 | 121 | if ((val == 0 || val == LONG_MAX) && errno == ERANGE) 122 | return -ERANGE; 123 | 124 | if (val > max) 125 | return -ERANGE; 126 | 127 | if (*endptr != '\0') 128 | return -EINVAL; 129 | 130 | *dest = val; 131 | return 0; 132 | } 133 | 134 | /** 135 | * is_file_executable 136 | */ 137 | static inline int is_file_executable(const char *filename) 138 | { 139 | struct stat sb; 140 | 141 | if (stat(filename, &sb) == -1) { 142 | perror("stat"); 143 | return 0; 144 | } 145 | 146 | if (S_ISREG(sb.st_mode) && 147 | (sb.st_mode & S_IRUSR) && (sb.st_mode & S_IXUSR)) 148 | return 1; 149 | 150 | return 0; 151 | } 152 | 153 | 154 | #define IP4_NMASK 32 155 | #define IP6_NMASK 128 156 | /** 157 | * split_ip_netmask() - split IPvX and netmask from a string 158 | */ 159 | static inline int split_ip_netmask(int family, 160 | const char *str, void *addr, 161 | uint8_t * netmask) 162 | { 163 | char *tmp; 164 | unsigned long ul; 165 | 166 | int netmask_length, err; 167 | 168 | /* IPv4 */ 169 | if (family == AF_INET) 170 | netmask_length = IP4_NMASK; 171 | 172 | /* IPv6 */ 173 | if (family == AF_INET6) 174 | netmask_length = IP6_NMASK; 175 | 176 | tmp = strstr(str, "/"); 177 | *netmask = 0; 178 | 179 | if (tmp != NULL) { 180 | *tmp = '\0'; 181 | ++tmp; 182 | err = mystrtoul(&ul, tmp, netmask_length); 183 | if (err == -ERANGE) { 184 | log_error("%s", "CIDR netmask out of range"); 185 | return err; 186 | } 187 | if (err == -EINVAL) { 188 | log_error("Error parsing %s as a number", tmp); 189 | return err; 190 | } 191 | 192 | if (netmask != NULL) 193 | *netmask = (uint8_t) ul; 194 | } 195 | 196 | if (inet_pton(family, str, addr) == 0) { 197 | log_error("inet_pton - %m"); 198 | return -1; 199 | } 200 | 201 | return 0; 202 | } 203 | 204 | 205 | #ifdef DEBUG 206 | /** 207 | * print buf hexa 208 | */ 209 | static inline void print_buf_hexa(const char *str, void *buf, size_t x) 210 | { 211 | uint8_t i; 212 | int j = 0; 213 | 214 | unsigned char *pbuf = (unsigned char *) buf; 215 | 216 | printf("*** %s ***\n", str); 217 | 218 | for (i = 0; i < x; ++i, ++j) { 219 | if (j == 4) { 220 | printf("\n"); 221 | j = 0; 222 | } 223 | if (j > 0) 224 | printf(":"); 225 | printf("%02X", pbuf[i]); 226 | } 227 | printf("\n"); 228 | } 229 | #endif 230 | 231 | #endif /* _COMMON_H_ */ 232 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | AC_PREREQ(2.50) 2 | AC_INIT(uvrrpd, 0.1, arno@ankhoon.net) 3 | AC_CONFIG_AUX_DIR([config]) 4 | AC_CONFIG_MACRO_DIR([config]) 5 | AM_INIT_AUTOMAKE([foreign dist-xz tar-ustar]) 6 | m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])]) 7 | AC_CONFIG_SRCDIR(uvrrpd.c) 8 | 9 | AC_PROG_CC 10 | AC_PROG_CC_STDC dnl for -std=gnu99 11 | AC_USE_SYSTEM_EXTENSIONS dnl for -D_GNU_SOURCE 12 | AC_PROG_LIBTOOL 13 | AC_SEARCH_LIBS([clock_gettime],[rt posix4]) 14 | 15 | AC_MSG_CHECKING(for debug options) 16 | 17 | DEBUG_OPTS="" 18 | AC_ARG_ENABLE(debug-logs, 19 | AS_HELP_STRING([--enable-debug-logs],[Enable debug logs]), 20 | [case "$enableval" in 21 | y | yes) DEBUG_OPTS="-DDEBUG";; 22 | esac]) 23 | 24 | AC_ARG_ENABLE(asserts, 25 | AS_HELP_STRING([--disable-asserts],[Disable run-time assertions]), 26 | [case "$enableval" in 27 | n | no) DEBUG_OPTS="$DEBUG_OPTS -DNDEBUG";; 28 | esac]) 29 | 30 | AC_MSG_RESULT(${DEBUG_OPTS:-none}) 31 | AC_SUBST(DEBUG_OPTS) 32 | 33 | AC_ARG_ENABLE(ipv6, 34 | AS_HELP_STRING([--disable-ipv6], 35 | [disable ipv6 support (default is autodetect)]), 36 | uvrrpd_want_ipv6=$enable_ipv6,) 37 | 38 | dnl check for ipv6 39 | if test x"uvrrpd_want_ipv6" != xno; then 40 | AC_CHECK_TYPES(struct in6_addr,,,[#include ]) 41 | AC_CHECK_TYPES(struct ip6_hdr,,,[#include ]) 42 | AC_CHECK_DECLS(AF_INET6,,,[#include ]) 43 | AC_MSG_CHECKING(for IPv6 headers and structures) 44 | uvrrpd_want_ipv6=no 45 | 46 | if test x"$ac_cv_type_struct_in6_addr" = xyes; then 47 | if test x"$ac_cv_type_struct_ip6_hdr" = xyes; then 48 | if test x"$ac_cv_have_decl_AF_INET6" = xyes; then 49 | uvrrpd_want_ipv6=yes 50 | fi 51 | fi 52 | fi 53 | AC_MSG_RESULT($uvrrpd_want_ipv6) 54 | fi 55 | 56 | dnl check for ipv6 multicast (required) 57 | if test x"uvrrpd_want_ipv6" != xno; then 58 | AC_CHECK_DECLS(IPV6_ADD_MEMBERSHIP,,,[#include ]) 59 | AC_CHECK_TYPES(struct ipv6_mreq,,,[#include ]) 60 | AC_MSG_CHECKING(for IPv6 multicast support) 61 | uvrrpd_want_ipv6_mcast=no 62 | 63 | if test x"$ac_cv_have_decl_IPV6_ADD_MEMBERSHIP" = xyes; then 64 | if test x"$ac_cv_type_struct_ipv6_mreq" = xyes; then 65 | uvrrpd_want_ipv6_mcast=yes 66 | AC_DEFINE([HAVE_IP6], 1, [Define to enable IPv6 support]) 67 | fi 68 | fi 69 | AC_MSG_RESULT($uvrrpd_want_ipv6_mcast) 70 | fi 71 | 72 | 73 | AC_CONFIG_FILES([ 74 | Makefile 75 | ]) 76 | 77 | AC_OUTPUT() 78 | -------------------------------------------------------------------------------- /list.h: -------------------------------------------------------------------------------- 1 | /** 2 | * list.h Copied from the Linux kernel source tree 3 | * 4 | * Implement doubly linked list (list_head) 5 | * 6 | * Licensed under the GPL v2 as per the whole kernel source tree. 7 | * 8 | */ 9 | 10 | #ifndef _LIB_LIST_H_ 11 | #define _LIB_LIST_H_ 12 | 13 | 14 | #include 15 | 16 | /*! 17 | * container_of - cast a member of a descriptor out to the containing descriptor 18 | * 19 | * @ptr: the pointer to the member. 20 | * @type: the type of the container struct this is embedded in. 21 | * @member: the name of the member within the struct. 22 | * 23 | */ 24 | #define container_of(ptr, type, member) ({ \ 25 | typeof( ((type *)0)->member ) *__mptr = (ptr); \ 26 | (type *)( (char *)__mptr - offsetof(type,member) );}) 27 | 28 | /*! 29 | * These are non-NULL pointers that will result in page faults 30 | * under normal circumstances, used to verify that nobody uses 31 | * non-initialized list entries. 32 | */ 33 | #define LIST_POISON1 ((void *) 0x00100100) 34 | #define LIST_POISON2 ((void *) 0x00200200) 35 | 36 | /*! 37 | * Simple doubly linked list implementation. 38 | * 39 | * Some of the internal functions ("__xxx") are useful when 40 | * manipulating whole lists rather than single entries, as 41 | * sometimes we already know the next/prev entries and we can 42 | * generate better code by using them directly rather than 43 | * using the generic single-entry routines. 44 | */ 45 | 46 | struct list_head { 47 | struct list_head *next, *prev; 48 | }; 49 | 50 | #define LIST_HEAD_INIT(name) { &(name), &(name) } 51 | 52 | #define LIST_HEAD(name) \ 53 | struct list_head name = LIST_HEAD_INIT(name) 54 | 55 | static inline void INIT_LIST_HEAD(struct list_head *list) 56 | { 57 | list->next = list; 58 | list->prev = list; 59 | } 60 | 61 | /*! 62 | * Insert a new entry between two known consecutive entries. 63 | * 64 | * This is only for internal list manipulation where we know 65 | * the prev/next entries already! 66 | */ 67 | static inline void 68 | __list_add(struct list_head *new, 69 | struct list_head *prev, struct list_head *next) 70 | { 71 | next->prev = new; 72 | new->next = next; 73 | new->prev = prev; 74 | prev->next = new; 75 | } 76 | 77 | /*! 78 | * list_add - add a new entry 79 | * @new: new entry to be added 80 | * @head: list head to add it after 81 | * 82 | * Insert a new entry after the specified head. 83 | * This is good for implementing stacks. 84 | */ 85 | static inline void list_add(struct list_head *new, struct list_head *head) 86 | { 87 | __list_add(new, head, head->next); 88 | } 89 | 90 | /*! 91 | * list_add_tail - add a new entry 92 | * @new: new entry to be added 93 | * @head: list head to add it before 94 | * 95 | * Insert a new entry before the specified head. 96 | * This is useful for implementing queues. 97 | */ 98 | static inline void list_add_tail(struct list_head *new, struct list_head *head) 99 | { 100 | __list_add(new, head->prev, head); 101 | } 102 | 103 | /*! 104 | * Delete a list entry by making the prev/next entries 105 | * point to each other. 106 | * 107 | * This is only for internal list manipulation where we know 108 | * the prev/next entries already! 109 | */ 110 | static inline void __list_del(struct list_head *prev, struct list_head *next) 111 | { 112 | next->prev = prev; 113 | prev->next = next; 114 | } 115 | 116 | /*! 117 | * list_del - deletes entry from list. 118 | * @entry: the element to delete from the list. 119 | * Note: list_empty on entry does not return true after this, the entry is 120 | * in an undefined state. 121 | */ 122 | static inline void list_del(struct list_head *entry) 123 | { 124 | __list_del(entry->prev, entry->next); 125 | entry->next = LIST_POISON1; 126 | entry->prev = LIST_POISON2; 127 | } 128 | 129 | /*! 130 | * list_del_init - deletes entry from list and reinitialize it. 131 | * @entry: the element to delete from the list. 132 | */ 133 | static inline void list_del_init(struct list_head *entry) 134 | { 135 | __list_del(entry->prev, entry->next); 136 | INIT_LIST_HEAD(entry); 137 | } 138 | 139 | /** 140 | * list_replace - replace old entry by new one 141 | * @old : the element to be replaced 142 | * @new : the new element to insert 143 | * 144 | * If @old was empty, it will be overwritten. 145 | */ 146 | static inline void list_replace(struct list_head *old, struct list_head *new) 147 | { 148 | new->next = old->next; 149 | new->next->prev = new; 150 | new->prev = old->prev; 151 | new->prev->next = new; 152 | } 153 | 154 | static inline void 155 | list_replace_init(struct list_head *old, struct list_head *new) 156 | { 157 | list_replace(old, new); 158 | INIT_LIST_HEAD(old); 159 | } 160 | 161 | /*! 162 | * list_swap - swap elements from list 163 | * @a: the entry to move to a 164 | * @b: the entry to move to b 165 | */ 166 | static inline void list_swap(struct list_head *a, struct list_head *b) 167 | { 168 | if (a->next == b) { 169 | list_replace(a, b); 170 | list_add(b, a); 171 | } 172 | else if (a->prev == b) { 173 | list_replace(b, a); 174 | list_add(a, b); 175 | } 176 | else { 177 | struct list_head tmp; 178 | 179 | tmp.next = b->next; 180 | tmp.prev = b->prev; 181 | list_replace(a, b); 182 | list_replace(&tmp, a); 183 | } 184 | } 185 | 186 | /*! 187 | * list_move - delete from one list and add as another's head 188 | * @list: the entry to move 189 | * @head: the head that will precede our entry 190 | */ 191 | static inline void list_move(struct list_head *list, struct list_head *head) 192 | { 193 | __list_del(list->prev, list->next); 194 | list_add(list, head); 195 | } 196 | 197 | /*! 198 | * list_move_tail - delete from one list and add as another's tail 199 | * @list: the entry to move 200 | * @head: the head that will follow our entry 201 | */ 202 | static inline void 203 | list_move_tail(struct list_head *list, struct list_head *head) 204 | { 205 | __list_del(list->prev, list->next); 206 | list_add_tail(list, head); 207 | } 208 | 209 | /** 210 | * list_is_last - tests whether @list is the last entry in list @head 211 | * @list: the entry to test 212 | * @head: the head of the list 213 | */ 214 | static inline int 215 | list_is_last(const struct list_head *list, const struct list_head *head) 216 | { 217 | return list->next == head; 218 | } 219 | 220 | /*! 221 | * list_empty - tests whether a list is empty 222 | * @head: the list to test. 223 | */ 224 | static inline int list_empty(struct list_head const *head) 225 | { 226 | return head->next == head; 227 | } 228 | 229 | static inline void __list_splice(struct list_head *list, struct list_head *head) 230 | { 231 | struct list_head *first = list->next; 232 | struct list_head *last = list->prev; 233 | struct list_head *at = head->next; 234 | 235 | first->prev = head; 236 | head->next = first; 237 | 238 | last->next = at; 239 | at->prev = last; 240 | } 241 | 242 | /*! 243 | * list_splice - join two lists 244 | * @list: the new list to add. 245 | * @head: the place to add it in the first list. 246 | */ 247 | static inline void list_splice(struct list_head *list, struct list_head *head) 248 | { 249 | if (!list_empty(list)) 250 | __list_splice(list, head); 251 | } 252 | 253 | /*! 254 | * list_splice_init - join two lists and reinitialise the emptied list. 255 | * @list: the new list to add. 256 | * @head: the place to add it in the first list. 257 | * 258 | * The list at @list is reinitialised 259 | */ 260 | static inline void 261 | list_splice_init(struct list_head *list, struct list_head *head) 262 | { 263 | if (!list_empty(list)) { 264 | __list_splice(list, head); 265 | INIT_LIST_HEAD(list); 266 | } 267 | } 268 | 269 | /*! 270 | * list_entry - get the struct for this entry 271 | * @ptr: the &struct list_head pointer. 272 | * @type: the type of the struct this is embedded in. 273 | * @member: the name of the list_struct within the struct. 274 | */ 275 | #define list_entry(ptr, type, member) \ 276 | container_of(ptr, type, member) 277 | 278 | /** 279 | * list_first_entry - get the first element from a list 280 | * @ptr: the list head to take the element from. 281 | * @type: the type of the struct this is embedded in. 282 | * @member: the name of the list_struct within the struct. 283 | * 284 | * Note, that list is expected to be not empty. 285 | */ 286 | #define list_first_entry(ptr, type, member) \ 287 | list_entry((ptr)->next, type, member) 288 | 289 | /*! 290 | * list_for_each - iterate over a list 291 | * @pos: the &struct list_head to use as a loop counter. 292 | * @head: the head for your list. 293 | */ 294 | #define list_for_each(pos, head) \ 295 | for (pos = (head)->next; pos != (head); pos = pos->next) 296 | 297 | /*! 298 | * list_for_each_prev - iterate over a list backwards 299 | * @pos: the &struct list_head to use as a loop counter. 300 | * @head: the head for your list. 301 | */ 302 | #define list_for_each_prev(pos, head) \ 303 | for (pos = (head)->prev; pos != (head); pos = pos->prev) 304 | 305 | /*! 306 | * list_for_each_safe - iterate over a list safe against removal of list entry 307 | * @pos: the &struct list_head to use as a loop counter. 308 | * @n: another &struct list_head to use as temporary storage 309 | * @head: the head for your list. 310 | */ 311 | #define list_for_each_safe(pos, n, head) \ 312 | for (pos = (head)->next, n = pos->next; pos != (head); \ 313 | pos = n, n = pos->next) 314 | 315 | /*! 316 | * list_for_each_entry - iterate over list of given type 317 | * @pos: the type * to use as a loop counter. 318 | * @head: the head for your list. 319 | * @member: the name of the list_struct within the struct. 320 | */ 321 | #define list_for_each_entry(pos, head, member) \ 322 | for (pos = list_entry((head)->next, typeof(*pos), member); \ 323 | &pos->member != (head); \ 324 | pos = list_entry(pos->member.next, typeof(*pos), member)) 325 | 326 | /*! 327 | * list_for_each_entry_reverse - iterate backwards over list of given type. 328 | * @pos: the type * to use as a loop counter. 329 | * @head: the head for your list. 330 | * @member: the name of the list_struct within the struct. 331 | */ 332 | #define list_for_each_entry_reverse(pos, head, member) \ 333 | for (pos = list_entry((head)->prev, typeof(*pos), member); \ 334 | &pos->member != (head); \ 335 | pos = list_entry(pos->member.prev, typeof(*pos), member)) 336 | 337 | /** 338 | * list_for_each_entry_continue - continue iteration over list of given type 339 | * @pos: the type * to use as a loop cursor. 340 | * @head: the head for your list. 341 | * @member: the name of the list_struct within the struct. 342 | * 343 | * Continue to iterate over list of given type, continuing after 344 | * the current position. 345 | */ 346 | #define list_for_each_entry_continue(pos, head, member) \ 347 | for (pos = list_entry(pos->member.next, typeof(*pos), member); \ 348 | &pos->member != (head); \ 349 | pos = list_entry(pos->member.next, typeof(*pos), member)) 350 | 351 | /** 352 | * list_for_each_entry_from - iterate over list of given type from the current point 353 | * @pos: the type * to use as a loop cursor. 354 | * @head: the head for your list. 355 | * @member: the name of the list_struct within the struct. 356 | * 357 | * Iterate over list of given type, continuing from current position. 358 | */ 359 | #define list_for_each_entry_from(pos, head, member) \ 360 | for (; &pos->member != (head); \ 361 | pos = list_entry(pos->member.next, typeof(*pos), member)) 362 | 363 | /*! 364 | * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry 365 | * @pos: the type * to use as a loop counter. 366 | * @n: another type * to use as temporary storage 367 | * @head: the head for your list. 368 | * @member: the name of the list_struct within the struct. 369 | */ 370 | #define list_for_each_entry_safe(pos, n, head, member) \ 371 | for (pos = list_entry((head)->next, typeof(*pos), member), \ 372 | n = list_entry(pos->member.next, typeof(*pos), member); \ 373 | &pos->member != (head); \ 374 | pos = n, n = list_entry(n->member.next, typeof(*n), member)) 375 | 376 | /** 377 | * list_for_each_entry_safe_continue 378 | * @pos: the type * to use as a loop cursor. 379 | * @n: another type * to use as temporary storage 380 | * @head: the head for your list. 381 | * @member: the name of the list_struct within the struct. 382 | * 383 | * Iterate over list of given type, continuing after current point, 384 | * safe against removal of list entry. 385 | */ 386 | #define list_for_each_entry_safe_continue(pos, n, head, member) \ 387 | for (pos = list_entry(pos->member.next, typeof(*pos), member), \ 388 | n = list_entry(pos->member.next, typeof(*pos), member); \ 389 | &pos->member != (head); \ 390 | pos = n, n = list_entry(n->member.next, typeof(*n), member)) 391 | 392 | /** 393 | * list_for_each_entry_safe_from 394 | * @pos: the type * to use as a loop cursor. 395 | * @n: another type * to use as temporary storage 396 | * @head: the head for your list. 397 | * @member: the name of the list_struct within the struct. 398 | * 399 | * Iterate over list of given type from current point, safe against 400 | * removal of list entry. 401 | */ 402 | #define list_for_each_entry_safe_from(pos, n, head, member) \ 403 | for (n = list_entry(pos->member.next, typeof(*pos), member); \ 404 | &pos->member != (head); \ 405 | pos = n, n = list_entry(n->member.next, typeof(*n), member)) 406 | 407 | /** 408 | * list_for_each_entry_safe_reverse 409 | * @pos: the type * to use as a loop cursor. 410 | * @n: another type * to use as temporary storage 411 | * @head: the head for your list. 412 | * @member: the name of the list_struct within the struct. 413 | * 414 | * Iterate backwards over list of given type, safe against removal 415 | * of list entry. 416 | */ 417 | #define list_for_each_entry_safe_reverse(pos, n, head, member) \ 418 | for (pos = list_entry((head)->prev, typeof(*pos), member), \ 419 | n = list_entry(pos->member.prev, typeof(*pos), member); \ 420 | &pos->member != (head); \ 421 | pos = n, n = list_entry(n->member.prev, typeof(*n), member)) 422 | 423 | 424 | #endif /* _LIB_LIST_H_ */ 425 | -------------------------------------------------------------------------------- /log.c: -------------------------------------------------------------------------------- 1 | /* 2 | * log.c 3 | * 4 | * Copyright (C) 2014 Arnaud Andre 5 | * 6 | * This file is part of uvrrpd. 7 | * 8 | * uvrrpd is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * uvrrpd is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with uvrrpd. If not, see . 20 | */ 21 | 22 | /* ISO C */ 23 | #include 24 | #include 25 | #include 26 | 27 | /* POSIX */ 28 | #include 29 | 30 | #include "common.h" 31 | 32 | #ifdef DEBUG 33 | int __log_trigger = LOG_DEBUG; 34 | #else 35 | int __log_trigger = LOG_NOTICE; 36 | #endif 37 | 38 | int log_trigger(char const *level) 39 | { 40 | /* 41 | * LOG_ERR error conditions 42 | * LOG_WARNING warning conditions 43 | * LOG_NOTICE normal, but significant, condition 44 | * LOG_INFO informational message 45 | * LOG_DEBUG 46 | */ 47 | if (level == NULL) { 48 | return __log_trigger; 49 | } 50 | else if (matches(level, "err")) { 51 | __log_trigger = LOG_ERR; 52 | } 53 | else if (matches(level, "warning")) { 54 | __log_trigger = LOG_WARNING; 55 | } 56 | else if (matches(level, "notice")) { 57 | __log_trigger = LOG_NOTICE; 58 | } 59 | else if (matches(level, "info")) { 60 | __log_trigger = LOG_INFO; 61 | } 62 | else if (matches(level, "debug")) { 63 | __log_trigger = LOG_DEBUG; 64 | } 65 | 66 | return __log_trigger; 67 | } 68 | 69 | void log_open(char const *app, char const *level) 70 | { 71 | openlog(app ? app : "-", LOG_PERROR | LOG_PID, LOG_DAEMON); 72 | log_trigger(level); 73 | } 74 | 75 | void log_close(void) 76 | { 77 | closelog(); 78 | } 79 | 80 | void log_it(int priority, char const *format, ...) 81 | { 82 | va_list ap; 83 | 84 | va_start(ap, format); 85 | vsyslog(priority, format, ap); 86 | va_end(ap); 87 | } 88 | -------------------------------------------------------------------------------- /log.h: -------------------------------------------------------------------------------- 1 | /* 2 | * log.h 3 | * 4 | * Copyright (C) 2014 Arnaud Andre 5 | * 6 | * This file is part of uvrrpd. 7 | * 8 | * uvrrpd is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * uvrrpd is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with uvrrpd. If not, see . 20 | */ 21 | 22 | #ifndef _LOG_H_ 23 | #define _LOG_H_ 24 | 25 | 26 | /* ISO C */ 27 | #include 28 | #include 29 | 30 | /* POSIX */ 31 | #include 32 | 33 | /** 34 | * log_trigger (char const *level) 35 | * Set up logs level trigger. If level is NULL return current level trigger 36 | * - err Error conditions 37 | * - warning Warning conditions 38 | * - notice Normal, but significant, condition 39 | * - info Informational message 40 | * - debug Debug message 41 | */ 42 | int log_trigger(char const *level); 43 | 44 | /** 45 | * log_open (char const *app, char const *level) 46 | * Open logs system. Define logs level trigger. 47 | * @app Application name 48 | * @level Logs level 49 | */ 50 | void log_open(char const *app, char const *level); 51 | 52 | /** 53 | * log_close( void ) 54 | * Close logs system. 55 | */ 56 | void log_close(void); 57 | 58 | /** 59 | * log_it( int priority, char const *format, ...) 60 | * Log message if priority is higher than level trigger 61 | * @priority Message 62 | * @format Log message 63 | * @... Arguments list 64 | */ 65 | void log_it(int priority, char const *format, ...) 66 | __attribute__ ((__format__(__printf__, 2, 3))); 67 | 68 | /** 69 | * log_error( fmt, ... ) 70 | * @fmt Log message 71 | * @... Arguments list 72 | */ 73 | #define log_error( fmt, ... ) \ 74 | do { \ 75 | log_it( LOG_ERR, "%s::%s "fmt, \ 76 | "error", __func__, ##__VA_ARGS__ ); \ 77 | } while ( 0 ) 78 | 79 | /** 80 | * log_sys_error( fmt, ... ) 81 | * @fmt Log message 82 | * @... Arguments list 83 | */ 84 | #define log_sys_error( fmt, ... ) \ 85 | do { \ 86 | log_it( LOG_ERR, "%s::%s "fmt" %s", \ 87 | "error", __func__, ##__VA_ARGS__, strerror(errno) ); \ 88 | } while ( 0 ) 89 | 90 | /** 91 | * log_warning( fmt, ... ) 92 | * @fmt Log message 93 | * @... Arguments list 94 | */ 95 | #define log_warning( fmt, ... ) \ 96 | do { \ 97 | log_it( LOG_WARNING, "%s::%s "fmt, \ 98 | "warning", __func__, ##__VA_ARGS__ ); \ 99 | } while ( 0 ) 100 | 101 | /** 102 | * log_notice( ... ) 103 | * @... Log message and arguments list 104 | */ 105 | #define log_notice( ... ) \ 106 | do { \ 107 | log_it( LOG_NOTICE, ##__VA_ARGS__ ); \ 108 | } while ( 0 ) 109 | 110 | /** 111 | * log_info( ... ) 112 | * Log message. Enable in info or higher level 113 | * @... Log message and arguments list 114 | */ 115 | #define log_info( ... ) \ 116 | do { \ 117 | extern int __log_trigger; \ 118 | \ 119 | if ( LOG_INFO <= __log_trigger ) \ 120 | log_it( LOG_INFO, ##__VA_ARGS__ ); \ 121 | } while ( 0 ) 122 | 123 | /** 124 | * log_debug( fmt, ... ) 125 | * Log message. Enable in debug level 126 | * @fmt Log message 127 | * @... Arguments list 128 | */ 129 | #define log_debug( fmt, ... ) \ 130 | do { \ 131 | extern int __log_trigger; \ 132 | \ 133 | if ( LOG_DEBUG <= __log_trigger ) \ 134 | log_it( LOG_DEBUG, "%s %s "fmt, \ 135 | "--- DEBUG ---", __func__, ##__VA_ARGS__ ); \ 136 | } while ( 0 ) 137 | 138 | #ifdef DEBUG 139 | /** 140 | * log_devel( fmt, ... ) 141 | * Log message. Enable in devel level 142 | * @fmt Log message 143 | * @... Arguments list 144 | */ 145 | #define log_devel( fmt, ... ) \ 146 | do { \ 147 | extern int __log_trigger; \ 148 | \ 149 | if ( LOG_DEBUG <= __log_trigger ) \ 150 | log_it( LOG_DEBUG, "%s %s( " fmt " )", \ 151 | "--- DEVEL ---", __func__, ##__VA_ARGS__ ); \ 152 | } while ( 0 ) 153 | #else 154 | #define log_devel( fmt, ... ) 155 | #endif /* DEBUG */ 156 | 157 | 158 | #endif /* _LOG_H_ */ 159 | -------------------------------------------------------------------------------- /uvrrpd.c: -------------------------------------------------------------------------------- 1 | /* 2 | * uvrrpd.c - main entry point, server initialization 3 | * 4 | * Copyright (C) 2014 Arnaud Andre 5 | * 6 | * This file is part of uvrrpd. 7 | * 8 | * uvrrpd is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * uvrrpd is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with uvrrpd. If not, see . 20 | */ 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #ifdef _POSIX_PRIORITY_SCHEDULING 29 | #include 30 | #else 31 | #warning "no sched rt" 32 | #endif 33 | 34 | #include "uvrrpd.h" 35 | #include "vrrp.h" 36 | #include "vrrp_net.h" 37 | #include "vrrp_adv.h" 38 | #include "vrrp_arp.h" 39 | #ifdef HAVE_IP6 40 | #include "vrrp_na.h" 41 | #endif 42 | #include "vrrp_options.h" 43 | #include "vrrp_exec.h" 44 | #include "vrrp_ctrl.h" 45 | 46 | #include "log.h" 47 | 48 | /* global constants */ 49 | unsigned long reg = 0UL; 50 | int background = 1; 51 | char *loglevel = NULL; 52 | char *pidfile_name = NULL; 53 | char *ctrlfile_name = NULL; 54 | 55 | /* local methods */ 56 | static void signal_handler(int sig); 57 | static void signal_setup(void); 58 | static int pidfile_init(int vrid); 59 | static void pidfile_unlink(void); 60 | static void pidfile_check(int vrid); 61 | static void pidfile(int vrid); 62 | static int ctrlfile_init(int vrid); 63 | static void ctrlfile_unlink(void); 64 | static void ctrlfile(int vrid, int *fd); 65 | 66 | /** 67 | * main() - entry point 68 | * 69 | * Declare VRRP instance, init daemon 70 | * and launch state machine 71 | */ 72 | int main(int argc, char *argv[]) 73 | { 74 | signal_setup(); 75 | 76 | /* Current VRRP instance */ 77 | struct vrrp vrrp; 78 | struct vrrp_net vnet; 79 | 80 | /* Init VRRP instance */ 81 | vrrp_init(&vrrp); 82 | vrrp_net_init(&vnet); 83 | 84 | /* cmdline options */ 85 | if (! !vrrp_options(&vrrp, &vnet, argc, argv)) 86 | exit(EXIT_FAILURE); 87 | 88 | /* pidfile init && check */ 89 | if (pidfile_init(vrrp.vrid) != 0) 90 | exit(EXIT_FAILURE); 91 | 92 | pidfile_check(vrrp.vrid); 93 | 94 | /* logs */ 95 | log_open("uvrrpd", (char const *) loglevel); 96 | 97 | /* init and open control file fifo */ 98 | ctrlfile_init(vrrp.vrid); 99 | ctrlfile(vrrp.vrid, &vrrp.ctrl.fd); 100 | if (vrrp_ctrl_init(&vrrp.ctrl) != 0) 101 | exit(EXIT_FAILURE); 102 | 103 | /* open sockets */ 104 | if ((vrrp_net_socket(&vnet) != 0) || (vrrp_net_socket_xmit(&vnet) != 0)) 105 | exit(EXIT_FAILURE); 106 | 107 | /* hook script */ 108 | if (vrrp_exec_init(&vrrp) != 0) 109 | exit(EXIT_FAILURE); 110 | 111 | /* advertisement pkt */ 112 | if (vrrp_adv_init(&vnet, &vrrp) != 0) 113 | exit(EXIT_FAILURE); 114 | 115 | /* net topology */ 116 | if (vnet.family == AF_INET) { 117 | if (vrrp_arp_init(&vnet) != 0) 118 | exit(EXIT_FAILURE); 119 | } 120 | #ifdef HAVE_IP6 121 | else if (vnet.family == AF_INET6) { 122 | if (vrrp_na_init(&vnet) != 0) 123 | exit(EXIT_FAILURE); 124 | } 125 | #endif 126 | 127 | /* daemonize */ 128 | if (background) { 129 | if (daemon(0, (log_trigger(NULL) > LOG_INFO)) != 0) { 130 | log_error("vrid %d :: daemon - %m", vrrp.vrid); 131 | exit(EXIT_FAILURE); 132 | } 133 | } 134 | else { 135 | if (chdir("/") != 0) { 136 | log_error("vrid %d :: chdir - %m", vrrp.vrid); 137 | exit(EXIT_FAILURE); 138 | } 139 | } 140 | 141 | /* pidfile */ 142 | pidfile(vrrp.vrid); 143 | 144 | /* lock procress's virtual address space into RAM */ 145 | mlockall(MCL_CURRENT | MCL_FUTURE); 146 | /* set SCHED_RR */ 147 | uvrrpd_sched_set(); 148 | 149 | /* process */ 150 | set_bit(KEEP_GOING, ®); 151 | while (test_bit(KEEP_GOING, ®) && !vrrp_process(&vrrp, &vnet)); 152 | 153 | /* shutdown */ 154 | vrrp_adv_cleanup(&vnet); 155 | 156 | if (vnet.family == AF_INET) 157 | vrrp_arp_cleanup(&vnet); 158 | #ifdef HAVE_IP6 159 | else /* AF_INET6 */ 160 | vrrp_na_cleanup(&vnet); 161 | #endif 162 | 163 | vrrp_cleanup(&vrrp); 164 | vrrp_exec_cleanup(&vrrp); 165 | vrrp_ctrl_cleanup(&vrrp.ctrl); 166 | vrrp_net_cleanup(&vnet); 167 | 168 | log_close(); 169 | free(loglevel); 170 | pidfile_unlink(); 171 | ctrlfile_unlink(); 172 | free(pidfile_name); 173 | 174 | munlockall(); 175 | 176 | return EXIT_SUCCESS; 177 | } 178 | 179 | 180 | /** 181 | * signal_handler - Signal handler 182 | */ 183 | static void signal_handler(int sig) 184 | { 185 | switch (sig) { 186 | case SIGHUP: 187 | log_notice("HUP to the init state"); 188 | set_bit(UVRRPD_RELOAD, ®); 189 | break; 190 | 191 | case SIGUSR1: 192 | case SIGUSR2: 193 | set_bit(UVRRPD_DUMP, ®); 194 | break; 195 | 196 | case SIGPIPE: 197 | log_notice("this is not a SIGPIPE"); 198 | set_bit(UVRRPD_LOGOUT, ®); 199 | break; 200 | 201 | case SIGINT: 202 | case SIGTERM: 203 | case SIGQUIT: 204 | log_notice("%s - exit daemon", strsignal(sig)); 205 | set_bit(UVRRPD_RELOAD, ®); 206 | clear_bit(KEEP_GOING, ®); 207 | break; 208 | 209 | case SIGCHLD: 210 | /* bleh */ 211 | break; 212 | 213 | default: 214 | log_error("%s %d", strsignal(sig), sig); 215 | break; 216 | } 217 | } 218 | 219 | /** 220 | * signal_setup 221 | * - register signal handler 222 | * - SIGTERM: shutdown daemon 223 | * - SIGHUP: reload daemon (switch to init state) 224 | * - SIGCHLD: notify end of task (vrrp_exec()) 225 | * - SIGUSR1: logs daemon context: vrrp_context() 226 | * - SIGUSR2: todo, same as USR1 for the moment 227 | * - SIGPIPE: socket write failure 228 | * 229 | * - blocked signal, unblocked them on select() syscall vrrp_process() 230 | */ 231 | static void signal_setup(void) 232 | { 233 | struct sigaction sa; 234 | 235 | /* setup signal */ 236 | memset(&sa, 0x00, sizeof(sa)); 237 | sa.sa_handler = signal_handler; 238 | sa.sa_flags = 0; 239 | 240 | sigaction(SIGINT, &sa, NULL); 241 | sigaction(SIGTERM, &sa, NULL); 242 | sigaction(SIGQUIT, &sa, NULL); 243 | sigaction(SIGHUP, &sa, NULL); 244 | sigaction(SIGCHLD, &sa, NULL); 245 | sigaction(SIGUSR1, &sa, NULL); 246 | sigaction(SIGUSR2, &sa, NULL); 247 | sigaction(SIGALRM, &sa, NULL); 248 | sigaction(SIGPIPE, &sa, NULL); 249 | 250 | /* setup signal mask */ 251 | sigemptyset(&sa.sa_mask); 252 | 253 | sigaddset(&sa.sa_mask, SIGINT); 254 | sigaddset(&sa.sa_mask, SIGTERM); 255 | sigaddset(&sa.sa_mask, SIGQUIT); 256 | sigaddset(&sa.sa_mask, SIGHUP); 257 | sigaddset(&sa.sa_mask, SIGUSR1); 258 | sigaddset(&sa.sa_mask, SIGUSR2); 259 | 260 | sigprocmask(SIG_BLOCK, &sa.sa_mask, NULL); 261 | } 262 | 263 | /** 264 | * pidfile_init() 265 | */ 266 | static int pidfile_init(int vrid) 267 | { 268 | int max_len = NAME_MAX + PATH_MAX; 269 | if (pidfile_name == NULL) { 270 | pidfile_name = malloc(max_len); 271 | if (pidfile_name == NULL) { 272 | log_error("vrid %d :: malloc - %m", vrid); 273 | return -1; 274 | } 275 | 276 | snprintf(pidfile_name, max_len, PIDFILE_NAME, vrid); 277 | } 278 | 279 | return 0; 280 | } 281 | 282 | /** 283 | * pidfile_unlink() - remove pidfile 284 | */ 285 | static void pidfile_unlink(void) 286 | { 287 | if (pidfile_name) 288 | unlink(pidfile_name); 289 | } 290 | 291 | /** 292 | * pidfile_check() 293 | */ 294 | static void pidfile_check(int vrid) 295 | { 296 | struct flock fl; 297 | int err, fd; 298 | 299 | fd = open(pidfile_name, O_RDONLY | O_CLOEXEC); 300 | if (fd < 0) { 301 | if (errno == ENOENT) 302 | return; 303 | fprintf(stderr, "vrid %d :: error opening PID file %s: %m\n", 304 | vrid, pidfile_name); 305 | exit(EXIT_FAILURE); 306 | } 307 | 308 | fl.l_type = F_WRLCK; 309 | fl.l_whence = SEEK_SET; 310 | fl.l_start = 0; 311 | fl.l_len = 0; 312 | 313 | err = fcntl(fd, F_GETLK, &fl); 314 | close(fd); 315 | if (err < 0) { 316 | fprintf(stderr, "vrid %d :: error getting PID file %s lock: %m", 317 | vrid, pidfile_name); 318 | exit(EXIT_FAILURE); 319 | } 320 | 321 | if (fl.l_type == F_UNLCK) 322 | return; 323 | 324 | fprintf(stderr, "vrid %d :: uvrrpd is already running (pid %d)\n", vrid, 325 | (int) fl.l_pid); 326 | exit(EXIT_FAILURE); 327 | } 328 | 329 | /** 330 | * pid_file() 331 | */ 332 | static void pidfile(int vrid) 333 | { 334 | struct flock fl; 335 | char buf[16]; 336 | int err, fd; 337 | 338 | fd = open(pidfile_name, 339 | O_WRONLY | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR); 340 | if (fd < 0) { 341 | log_error("vrid %d :: error opening PID file %s: %m", vrid, 342 | pidfile_name); 343 | exit(EXIT_FAILURE); 344 | } 345 | 346 | fl.l_type = F_WRLCK; 347 | fl.l_whence = SEEK_SET; 348 | fl.l_start = 0; 349 | fl.l_len = 0; 350 | 351 | err = fcntl(fd, F_SETLK, &fl); 352 | if (err < 0) { 353 | if (errno == EACCES || errno == EAGAIN) { 354 | log_error("vrid %d :: uvrrpd is already running", 355 | vrid); 356 | exit(EXIT_FAILURE); 357 | } 358 | log_error("vrid %d :: error setting PID file %s lock: %m", 359 | vrid, pidfile_name); 360 | exit(EXIT_FAILURE); 361 | } 362 | 363 | atexit(pidfile_unlink); 364 | 365 | err = snprintf(buf, sizeof(buf), "%d\n", (int) getpid()); 366 | if (err < 0) { 367 | perror("snprintf"); 368 | exit(EXIT_FAILURE); 369 | } 370 | 371 | err = write(fd, buf, err); 372 | if (err < 0) { 373 | log_error("vrid %d :: error writing PID to PID file %s: %m", 374 | vrid, pidfile_name); 375 | exit(EXIT_FAILURE); 376 | } 377 | } 378 | 379 | 380 | /** 381 | * ctrlfile_init() 382 | */ 383 | static int ctrlfile_init(int vrid) 384 | { 385 | int max_len = NAME_MAX + PATH_MAX; 386 | if (ctrlfile_name == NULL) { 387 | ctrlfile_name = malloc(max_len); 388 | if (ctrlfile_name == NULL) { 389 | log_error("vrid %d :: malloc - %m", vrid); 390 | return -1; 391 | } 392 | 393 | snprintf(ctrlfile_name, max_len, CTRLFILE_NAME, vrid); 394 | } 395 | 396 | return 0; 397 | } 398 | 399 | /** 400 | * ctrlfile_unlink() 401 | */ 402 | static void ctrlfile_unlink() 403 | { 404 | if (ctrlfile_name) 405 | unlink(ctrlfile_name); 406 | } 407 | 408 | /** 409 | * ctrlfile() 410 | */ 411 | static void ctrlfile(int vrid, int *fd) 412 | { 413 | if (fd == NULL) { 414 | log_error("vrid %d :: invalid use of ctrlfile(), fd NULL", vrid); 415 | exit(EXIT_FAILURE); 416 | } 417 | 418 | ctrlfile_unlink(); 419 | if (mkfifo(ctrlfile_name, 0600) != 0) { 420 | log_error("vrid %d :: error while creating control fifo %s: %m", vrid, 421 | ctrlfile_name); 422 | exit(EXIT_FAILURE); 423 | } 424 | 425 | atexit(ctrlfile_unlink); 426 | 427 | *fd = open(ctrlfile_name, O_RDWR | O_NONBLOCK); 428 | if (*fd == -1) { 429 | log_error("vrid %d :: error while opening control fifo %s: %m", vrid, 430 | ctrlfile_name); 431 | exit(EXIT_FAILURE); 432 | } 433 | } 434 | 435 | /** 436 | * uvrrpd_sched_set() - set SCHED_RR scheduler 437 | */ 438 | int uvrrpd_sched_set() 439 | { 440 | #ifdef _POSIX_PRIORITY_SCHEDULING 441 | struct sched_param param; 442 | 443 | param.sched_priority = sched_get_priority_max(SCHED_RR); 444 | if (sched_setscheduler(0, SCHED_RR, ¶m) != 0) { 445 | log_error("sched_setscheduler() - %m"); 446 | return -1; 447 | } 448 | #else 449 | nice(-20); 450 | #endif 451 | 452 | return 0; 453 | } 454 | 455 | /** 456 | * uvrrpd_sched_unset() - unset SCHED_RR scheduler 457 | */ 458 | int uvrrpd_sched_unset() 459 | { 460 | #ifdef _POSIX_PRIORITY_SCHEDULING 461 | struct sched_param param; 462 | 463 | param.sched_priority = sched_get_priority_max(SCHED_OTHER); 464 | if (sched_setscheduler(0, SCHED_OTHER, ¶m) != 0) { 465 | log_error("sched_setscheduler() - %m"); 466 | return -1; 467 | } 468 | #endif 469 | return 0; 470 | } 471 | -------------------------------------------------------------------------------- /uvrrpd.h: -------------------------------------------------------------------------------- 1 | /* 2 | * uvrrpd.h 3 | * 4 | * Copyright (C) 2014 Arnaud Andre 5 | * 6 | * This file is part of uvrrpd. 7 | * 8 | * uvrrpd is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * uvrrpd is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with uvrrpd. If not, see . 20 | */ 21 | 22 | #ifndef _UVRRPD_H_ 23 | #define _UVRRPD_H_ 24 | 25 | #include "bits.h" 26 | 27 | #define PIDFILE_NAME stringify(PATHRUN) "/uvrrpd_%d.pid" 28 | #define CTRLFILE_NAME stringify(PATHRUN) "/uvrrpd_ctrl.%d" 29 | 30 | /** 31 | * uvrrpd_control 32 | * Enum server control register flags 33 | */ 34 | enum uvrrpd_control { 35 | /* daemon keep going bit */ 36 | KEEP_GOING = BIT_MASK(0), 37 | /* daemon dump bit */ 38 | UVRRPD_DUMP = BIT_MASK(1), 39 | /* daemon logout bit */ 40 | UVRRPD_LOGOUT = BIT_MASK(2), 41 | /* daemon reload bit */ 42 | UVRRPD_RELOAD = BIT_MASK(3), 43 | }; 44 | 45 | int uvrrpd_sched_set(void); 46 | int uvrrpd_sched_unset(void); 47 | 48 | #endif /* _UVRRPD_ */ 49 | -------------------------------------------------------------------------------- /vrrp.c: -------------------------------------------------------------------------------- 1 | /* 2 | * vrrp.c - init VRRP instance and VRRP state functions 3 | * 4 | * Copyright (C) 2014 Arnaud Andre 5 | * 6 | * This file is part of uvrrpd. 7 | * 8 | * uvrrpd is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * uvrrpd is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with uvrrpd. If not, see . 20 | */ 21 | 22 | #include 23 | /* pselect() */ 24 | #include 25 | #include 26 | 27 | #include "vrrp.h" 28 | #include "vrrp_timer.h" 29 | #include "vrrp_net.h" 30 | #include "vrrp_state.h" 31 | #include "vrrp_ctrl.h" 32 | 33 | #include "uvrrpd.h" 34 | #include "bits.h" 35 | 36 | #include "log.h" 37 | 38 | extern unsigned long reg; 39 | 40 | /** 41 | * vrrp_init() - init struct vrrp with default values 42 | */ 43 | void vrrp_init(struct vrrp *vrrp) 44 | { 45 | /* VRRP version */ 46 | vrrp->version = RFC3768; 47 | 48 | vrrp->vrid = 0; 49 | vrrp->priority = PRIO_DFL; 50 | vrrp->naddr = 0; 51 | 52 | /* auth */ 53 | vrrp->auth_type = NOAUTH; 54 | vrrp->auth_data = NULL; 55 | 56 | /* state */ 57 | vrrp->state = INIT; 58 | vrrp->preempt = PREEMPT_DFL; 59 | 60 | /* script */ 61 | vrrp->scriptname = NULL; 62 | vrrp->argv = NULL; 63 | 64 | /* timers */ 65 | vrrp->adv_int = 0; 66 | vrrp->start_delay = 0; 67 | vrrp->master_adv_int = 0; 68 | vrrp_timer_clear(&vrrp->adv_timer); 69 | vrrp_timer_clear(&vrrp->masterdown_timer); 70 | } 71 | 72 | /** 73 | * vrrp_context() - dump vrrp info 74 | */ 75 | static void vrrp_context(struct vrrp *vrrp) 76 | { 77 | log_notice("===================="); 78 | log_notice("VRID %d", vrrp->vrid); 79 | log_notice("current_state %s", STR_STATE(vrrp->state)); 80 | log_notice("priority %d", vrrp->priority); 81 | log_notice("adv_int %d", vrrp->adv_int); 82 | if (vrrp->version == RFC5798) 83 | log_notice("master_adv_int %d", vrrp->master_adv_int); 84 | log_notice("preempt %s", STR_PREEMPT(vrrp->preempt)); 85 | log_notice("naddr %d", vrrp->naddr); 86 | log_notice("===================="); 87 | } 88 | 89 | 90 | /** 91 | * vrrp_process() - vrrp control and state machine 92 | */ 93 | int vrrp_process(struct vrrp *vrrp, struct vrrp_net *vnet) 94 | { 95 | switch (vrrp->state) { 96 | case INIT: 97 | vrrp_state_init(vrrp, vnet); 98 | break; 99 | 100 | case BACKUP: 101 | vrrp_state_backup(vrrp, vnet); 102 | break; 103 | 104 | case MASTER: 105 | vrrp_state_master(vrrp, vnet); 106 | break; 107 | 108 | default: 109 | /* invalid state */ 110 | return -ENOSYS; 111 | break; 112 | } 113 | 114 | if (test_and_clear_bit(UVRRPD_DUMP, ®)) 115 | vrrp_context(vrrp); 116 | 117 | return 0; 118 | } 119 | 120 | /** 121 | * vrrp_listen() - Wait for a event (VRRP pkt, msg on fifo ...) 122 | * 123 | * @return vrrp_event_t 124 | * TIMER if current timer is expired 125 | * another event else 126 | */ 127 | vrrp_event_t vrrp_listen(struct vrrp *vrrp, struct vrrp_net *vnet) 128 | { 129 | struct vrrp_timer *vt; 130 | int max_fd; 131 | 132 | /* Check which timer is running 133 | * Advertisement timer or Masterdown timer ? 134 | */ 135 | if (vrrp_timer_is_running(&vrrp->adv_timer)) { 136 | log_debug("vrid %d :: adv_timer is running", vrrp->vrid); 137 | vt = &vrrp->adv_timer; 138 | } 139 | else if (vrrp_timer_is_running(&vrrp->masterdown_timer)) { 140 | log_debug("vrid %d :: masterdown_timer is running", vrrp->vrid); 141 | vt = &vrrp->masterdown_timer; 142 | } 143 | else { /* No timer ? ... exit */ 144 | log_error("vrid %d :: no timer running !", vrrp->vrid); 145 | /* TODO die() */ 146 | exit(EXIT_FAILURE); 147 | } 148 | 149 | /* update timer before pselect() */ 150 | if (vrrp_timer_update(vt)) { 151 | log_debug("vrid %d :: timer expired before pselect", 152 | vrrp->vrid); 153 | /* timer expired or invalid */ 154 | return TIMER; 155 | } 156 | 157 | /* pselect */ 158 | fd_set readfds; 159 | FD_ZERO(&readfds); 160 | FD_SET(vnet->socket, &readfds); 161 | FD_SET(vrrp->ctrl.fd, &readfds); 162 | max_fd = max(vnet->socket, vrrp->ctrl.fd); 163 | 164 | sigset_t emptyset; 165 | sigemptyset(&emptyset); 166 | 167 | /* Wait for packet or timer expiration */ 168 | if (pselect 169 | (max_fd + 1, &readfds, NULL, NULL, 170 | (const struct timespec *) &vt->delta, &emptyset) >= 0) { 171 | 172 | 173 | /* Timer is expired */ 174 | if (vrrp_timer_is_expired(vt)) { 175 | log_debug("vrid %d :: timer expired", vrrp->vrid); 176 | return TIMER; 177 | } 178 | 179 | /* Else we have received a pkt */ 180 | log_debug("vrid %d :: VRRP pkt received", vrrp->vrid); 181 | 182 | if (FD_ISSET(vnet->socket, &readfds)) 183 | /* check if received is valid or not */ 184 | return vrrp_net_recv(vnet, vrrp); 185 | 186 | if (FD_ISSET(vrrp->ctrl.fd, &readfds)) { 187 | return vrrp_ctrl_read(vrrp, vnet); 188 | } 189 | } 190 | else { /* Signal or pselect error */ 191 | if (errno == EINTR) { 192 | log_debug("vrid %d :: signal caught", vrrp->vrid); 193 | 194 | return SIGNAL; 195 | } 196 | 197 | log_error("vrid %d :: pselect - %m", vrrp->vrid); 198 | } 199 | 200 | return INVALID; 201 | } 202 | 203 | 204 | /** 205 | * vrrp_cleanup() - clean before exiting 206 | */ 207 | void vrrp_cleanup(struct vrrp *vrrp) 208 | { 209 | free(vrrp->scriptname); 210 | free(vrrp->auth_data); 211 | } 212 | -------------------------------------------------------------------------------- /vrrp.h: -------------------------------------------------------------------------------- 1 | /* 2 | * vrrp.h - define main struct vrrp (VRRP instance) and some 3 | * constants 4 | * 5 | * Copyright (C) 2014 Arnaud Andre 6 | * 7 | * This file is part of uvrrpd. 8 | * 9 | * uvrrpd is free software: you can redistribute it and/or modify 10 | * it under the terms of the GNU General Public License as published by 11 | * the Free Software Foundation, either version 3 of the License, or 12 | * (at your option) any later version. 13 | * 14 | * uvrrpd is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU General Public License 20 | * along with uvrrpd. If not, see . 21 | */ 22 | 23 | #ifndef _VRRP_H_ 24 | #define _VRRP_H_ 25 | 26 | #include 27 | 28 | #include "common.h" 29 | #include "vrrp_net.h" 30 | #include "vrrp_timer.h" 31 | #include "vrrp_state.h" 32 | #include "vrrp_ctrl.h" 33 | 34 | /* MAX values */ 35 | #define VRID_MAX 255 36 | #define VRRP_PRIO_MAX 255 37 | #define ADVINT_MAX 4095 /* RFC5798 */ 38 | #define PRIO_OWNER VRRP_PRIO_MAX 39 | 40 | /* DEFAULT values */ 41 | #define PREEMPT_DFL TRUE 42 | #define PRIO_DFL 100 43 | 44 | /* External script */ 45 | #define VRRP_SCRIPT stringify(PATH) "/vrrp_switch.sh" 46 | #define VRRP_SCRIPT_MAX sysconf(_SC_ARG_MAX) 47 | 48 | /* preemption */ 49 | #define STR_PREEMPT(s) (s == TRUE ? "true" : "false") 50 | 51 | /** 52 | * vrrp_version - VRRP version protocol 53 | * @RFC2338 : VRRPv2 (deprecated) 54 | * @RFC3768 : VRRPv2 55 | * @RFC5798 : VRRPv3 56 | */ 57 | typedef enum { 58 | RFC2338 = 2, 59 | RFC3768 = 2, 60 | RFC5798 = 3 61 | } vrrp_version; 62 | 63 | /** 64 | * vrrp_authtype - Authentication type 65 | * (from VRRPv2 / rfc2332) 66 | */ 67 | #define VRRP_AUTH_PASS_LEN 8 68 | typedef enum { 69 | NOAUTH, 70 | SIMPLE, 71 | HMAC /* not supported */ 72 | } vrrp_authtype; 73 | 74 | /** 75 | * vrrp - Main structure defining VRRP instance 76 | */ 77 | struct vrrp { 78 | vrrp_version version; /* VRRP version */ 79 | uint8_t vrid; /* VRID 1 - 255 */ 80 | uint8_t priority; /* PRIO 0 - 255 */ 81 | uint8_t naddr; /* count ip addresses */ 82 | 83 | /* Advertisement interval 84 | * 85 | * VRRPv2 : 86 | * - delay in s 87 | * - default 1s 88 | * - 8 bits field 89 | * 90 | * VRRPv3 : 91 | * - delay in centisecond 92 | * - default 100cs (1s) 93 | * - 12 bits field 94 | */ 95 | uint16_t adv_int; 96 | 97 | /* Start delay */ 98 | uint16_t start_delay; 99 | 100 | /* Master advertisement interval 101 | * only in VRRPv3 / rfc5798 102 | */ 103 | uint16_t master_adv_int; 104 | 105 | vrrp_authtype auth_type; 106 | char *auth_data; 107 | 108 | vrrp_state state; 109 | bool preempt; 110 | 111 | char *scriptname; 112 | char **argv; 113 | 114 | /* control cmd fifo */ 115 | struct vrrp_ctrl ctrl; 116 | 117 | struct vrrp_timer adv_timer; 118 | struct vrrp_timer masterdown_timer; 119 | }; 120 | 121 | /** 122 | * enum vrrp_ret - Return code used in vrrp_net_listen 123 | */ 124 | enum _vrrp_event_type { 125 | VRID_MISMATCH = -2, /* vrid mismatch */ 126 | INVALID = -1, /* invalid pkt */ 127 | PKT, /* valid packet */ 128 | SIGNAL, /* signal catch */ 129 | CTRL_FIFO, /* ctrl cmd event */ 130 | TIMER /* timer expired */ 131 | }; 132 | 133 | typedef enum _vrrp_event_type vrrp_event_t; 134 | /* funcs */ 135 | void vrrp_init(struct vrrp *vrrp); 136 | void vrrp_cleanup(struct vrrp *vrrp); 137 | int vrrp_process(struct vrrp *vrrp, struct vrrp_net *vnet); 138 | vrrp_event_t vrrp_listen(struct vrrp *vrrp, struct vrrp_net *vnet); 139 | 140 | #endif /* _VRRP_H_ */ 141 | -------------------------------------------------------------------------------- /vrrp_adv.c: -------------------------------------------------------------------------------- 1 | /* 2 | * vrrp_adv.c - VRRP advertisement 3 | * 4 | * Copyright (C) 2014 Arnaud Andre 5 | * 6 | * This file is part of uvrrpd. 7 | * 8 | * uvrrpd is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * uvrrpd is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with uvrrpd. If not, see . 20 | */ 21 | 22 | #include 23 | #include 24 | #include 25 | #include // ETH_P_IP = 0x0800, ETH_P_IPV6 = 0x86DD 26 | #include 27 | #include 28 | #ifdef HAVE_IP6 29 | #include 30 | #endif 31 | 32 | #include "log.h" 33 | #include "vrrp.h" 34 | #include "vrrp_net.h" 35 | #include "vrrp_rfc.h" 36 | 37 | /* VRRP multicast group */ 38 | #define INADDR_VRRP_GROUP 0xe0000012 /* 224.0.0.18 */ 39 | 40 | #ifdef HAVE_IP6 41 | #define IN6ADDR_VRRP_GROUP "FF02::12" 42 | #endif 43 | 44 | #define ETHDR_SIZE sizeof(struct ether_header) 45 | 46 | #define VRRP_TYPE_ADV 1 47 | 48 | /** 49 | * ether_header vrrp_adv_eth - VRRP ethernet header 50 | */ 51 | static struct ether_header vrrp_adv_eth = { 52 | .ether_dhost = {0x01, 53 | 0x00, 54 | 0x5e, 55 | (INADDR_VRRP_GROUP >> 16) & 0x7F, 56 | (INADDR_VRRP_GROUP >> 8) & 0xFF, 57 | INADDR_VRRP_GROUP & 0xFF}, 58 | .ether_shost = {0x00, 59 | 0x00, 60 | 0x5e, 61 | 0x00, 62 | 0x01, 63 | 0x00}, /* vrrp->vrid */ 64 | }; 65 | 66 | /** 67 | * vrrp_adv_eth_build() - build VRRP adv ethernet header 68 | */ 69 | static int vrrp_adv_eth_build(struct iovec *iov, const uint8_t vrid, 70 | const int family) 71 | { 72 | iov->iov_base = malloc(sizeof(struct ether_header)); 73 | struct ether_header *hdr = iov->iov_base; 74 | 75 | if (hdr == NULL) { 76 | log_error("vrid %d :: malloc - %m", vrid); 77 | return -1; 78 | } 79 | 80 | memcpy(hdr, &vrrp_adv_eth, sizeof(struct ether_header)); 81 | hdr->ether_shost[5] = vrid; 82 | if (family == AF_INET) 83 | hdr->ether_type = htons(ETH_P_IP); 84 | #ifdef HAVE_IP6 85 | else /* AF_INET6 */ 86 | hdr->ether_type = htons(ETH_P_IPV6); 87 | #endif 88 | iov->iov_len = ETHDR_SIZE; 89 | 90 | return 0; 91 | } 92 | 93 | /** 94 | * vrrp_adv_ip4_build() - build VRRP IPv4 advertisement 95 | */ 96 | static int vrrp_adv_ip4_build(struct iovec *iov, const struct vrrp_net *vnet) 97 | { 98 | iov->iov_base = malloc(sizeof(struct iphdr)); 99 | 100 | struct iphdr *iph = iov->iov_base; 101 | 102 | if (iph == NULL) { 103 | log_error("vrid %d :: malloc - %m", vnet->vrid); 104 | return -1; 105 | } 106 | 107 | iph->ihl = 0x5; 108 | iph->version = IPVERSION; 109 | iph->tos = 0x00; 110 | iph->tot_len = htons(IPHDR_SIZE + vnet->adv_getsize(vnet)); 111 | iph->id = htons(0xdead); 112 | iph->ttl = 0xff; /* VRRP_TTL */ 113 | iph->frag_off = 0x00; 114 | iph->protocol = IPPROTO_VRRP; 115 | 116 | iph->saddr = vnet->vif.ip_addr.s_addr; 117 | iph->daddr = htonl(INADDR_VRRP_GROUP); 118 | 119 | iph->check = 0; 120 | iph->check = cksum((unsigned short *) iph, IPHDR_SIZE); 121 | 122 | iov->iov_len = IPHDR_SIZE; 123 | 124 | return 0; 125 | } 126 | 127 | /** 128 | * vrrp_adv_ip6_build() - build VRRP IPv6 advertisement 129 | */ 130 | #ifdef HAVE_IP6 131 | static int vrrp_adv_ip6_build(struct iovec *iov, const struct vrrp_net *vnet) 132 | { 133 | iov->iov_base = malloc(sizeof(struct ip6_hdr)); 134 | 135 | struct ip6_hdr *ip6h = iov->iov_base; 136 | 137 | if (ip6h == NULL) { 138 | log_error("vrid %d :: malloc - %m", vnet->vrid); 139 | return -1; 140 | } 141 | 142 | ip6h->ip6_flow = htonl((6 << 28) | (0 << 20) | 0); 143 | ip6h->ip6_plen = htons(vnet->adv_getsize(vnet)); 144 | ip6h->ip6_nxt = IPPROTO_VRRP; 145 | ip6h->ip6_hlim = 0xff; /* VRRP_TTL */ 146 | 147 | memcpy(&ip6h->ip6_src, &vnet->vif.ip_addr6, sizeof(struct in6_addr)); 148 | 149 | if (inet_pton(AF_INET6, IN6ADDR_VRRP_GROUP, &(ip6h->ip6_dst)) != 1) { 150 | log_error("vrid %d :: inet_pton - %m", vnet->vrid); 151 | return -1; 152 | } 153 | 154 | iov->iov_len = sizeof(struct ip6_hdr); 155 | 156 | return 0; 157 | } 158 | #endif /* HAVE_IP6 */ 159 | 160 | /** 161 | * vrrp_net_adv_build() - build VRRP adv pkt 162 | */ 163 | static int vrrp_adv_build(struct iovec *iov, const struct vrrp_net *vnet, 164 | const struct vrrp *vrrp) 165 | { 166 | iov->iov_base = malloc(vnet->adv_getsize(vnet)); 167 | 168 | struct vrrphdr *pkt = iov->iov_base; 169 | 170 | if (pkt == NULL) { 171 | log_error("vrid %d :: malloc - %m", vnet->vrid); 172 | return -1; 173 | } 174 | 175 | pkt->version_type = (vrrp->version << 4) | VRRP_TYPE_ADV; 176 | pkt->vrid = vnet->vrid; 177 | pkt->priority = vrrp->priority; 178 | pkt->naddr = vrrp->naddr; 179 | 180 | if (vrrp->version == RFC3768) { 181 | pkt->auth_type = vrrp->auth_type; 182 | pkt->adv_int = vrrp->adv_int; 183 | } 184 | else if (vrrp->version == RFC5798) { 185 | pkt->max_adv_int = htons(vrrp->adv_int); 186 | } 187 | 188 | /* write vrrp_ip addresses */ 189 | uint32_t *vip_addr = 190 | (uint32_t *) ((unsigned char *) pkt + VRRP_PKTHDR_SIZE); 191 | 192 | struct vrrp_ip *vip_ptr = NULL; 193 | uint32_t pos = 0; 194 | int naddr = 0; 195 | 196 | list_for_each_entry_reverse(vip_ptr, &vnet->vip_list, iplist) { 197 | 198 | if (vnet->family == AF_INET) { 199 | vip_addr[pos] = vip_ptr->ip_addr.s_addr; 200 | ++pos; 201 | } 202 | #ifdef HAVE_IP6 203 | else { /* AF_INET6 */ 204 | memcpy(&vip_addr[pos], &vip_ptr->ip_addr6, 205 | sizeof(struct in6_addr)); 206 | pos += 4; 207 | } 208 | #endif /* HAVE_IP6 */ 209 | ++naddr; 210 | 211 | if (naddr > vrrp->naddr) { 212 | log_error 213 | ("vrid %d :: Build invalid avd pkt, try to write more vip than expected", 214 | vnet->vrid); 215 | return -1; 216 | } 217 | }; 218 | 219 | /* auth password */ 220 | if ((vrrp->version == RFC2338) && (vrrp->auth_type == SIMPLE) 221 | && (vrrp->auth_data != NULL)) { 222 | uint32_t *auth_data = vip_addr + pos; 223 | memcpy(auth_data, vrrp->auth_data, strlen(vrrp->auth_data)); 224 | } 225 | 226 | /* chksum */ 227 | pkt->chksum = vnet->adv_checksum(vnet, pkt, NULL, NULL); 228 | 229 | /* iov_len */ 230 | iov->iov_len = vnet->adv_getsize(vnet); 231 | 232 | return 0; 233 | } 234 | 235 | /** 236 | * vrrp_adv_send() - send VRRP adv pkt 237 | */ 238 | int vrrp_adv_send(struct vrrp_net *vnet) 239 | { 240 | return vrrp_net_send(vnet, vnet->__adv, ARRAY_SIZE(vnet->__adv)); 241 | } 242 | 243 | /** 244 | * vrrp_adv_send_zero() - send VRRP adv pkt with priority 0 245 | */ 246 | int vrrp_adv_send_zero(struct vrrp_net *vnet) 247 | { 248 | /* get adv buffer */ 249 | struct iovec *iov = &vnet->__adv[2]; 250 | struct vrrphdr *pkt = iov->iov_base; 251 | 252 | /* set priority to 0 and recompute checksum */ 253 | uint8_t priority = pkt->priority; 254 | pkt->priority = 0; 255 | uint16_t chksum = pkt->chksum; 256 | 257 | /* chksum */ 258 | pkt->chksum = vnet->adv_checksum(vnet, pkt, NULL, NULL); 259 | 260 | /* send adv pkt */ 261 | int ret = vrrp_net_send(vnet, vnet->__adv, ARRAY_SIZE(vnet->__adv)); 262 | 263 | /* restaure original priority and checksum */ 264 | pkt->priority = priority; 265 | pkt->chksum = chksum; 266 | 267 | return ret; 268 | } 269 | 270 | /** 271 | * vrrp_adv_init() - init advertisement pkt to send 272 | */ 273 | int vrrp_adv_init(struct vrrp_net *vnet, const struct vrrp *vrrp) 274 | { 275 | int status = -1; 276 | 277 | status = vrrp_adv_eth_build(&vnet->__adv[0], vnet->vrid, vnet->family); 278 | 279 | if (vnet->family == AF_INET) 280 | status |= vrrp_adv_ip4_build(&vnet->__adv[1], vnet); 281 | #ifdef HAVE_IP6 282 | else /* AF_INET6 */ 283 | status |= vrrp_adv_ip6_build(&vnet->__adv[1], vnet); 284 | #endif /* HAVE_IP6 */ 285 | 286 | status |= vrrp_adv_build(&vnet->__adv[2], vnet, vrrp); 287 | 288 | return status; 289 | } 290 | 291 | /** 292 | * vrrp_adv_cleanup() 293 | */ 294 | void vrrp_adv_cleanup(struct vrrp_net *vnet) 295 | { 296 | /* clean iovec */ 297 | for (int i = 0; i < 3; ++i) { 298 | struct iovec *iov = &vnet->__adv[i]; 299 | free(iov->iov_base); 300 | } 301 | } 302 | -------------------------------------------------------------------------------- /vrrp_adv.h: -------------------------------------------------------------------------------- 1 | /* 2 | * vrrp_adv.h - VRRP advertisement 3 | * 4 | * Copyright (C) 2014 Arnaud Andre 5 | * 6 | * This file is part of uvrrpd. 7 | * 8 | * uvrrpd is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * uvrrpd is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with uvrrpd. If not, see . 20 | */ 21 | 22 | #ifndef _VRRP_ADV_H_ 23 | #define _VRRP_ADV_H_ 24 | 25 | 26 | int vrrp_adv_init(struct vrrp_net *vnet, const struct vrrp *vrrp); 27 | void vrrp_adv_cleanup(struct vrrp_net *vnet); 28 | int vrrp_adv_send(struct vrrp_net *vnet); 29 | int vrrp_adv_send_zero(struct vrrp_net *vnet); 30 | uint16_t vrrp_adv_chksum(struct vrrp_net *vnet, struct vrrphdr *pkt, 31 | uint32_t saddr, uint32_t daddr); 32 | #ifdef HAVE_IP6 33 | uint16_t vrrp_adv_ip6_chksum(struct vrrp_net *vnet, struct vrrphdr *pkt, 34 | struct in6_addr *saddr, struct in6_addr *daddr); 35 | #endif /* HAVE_IP6 */ 36 | 37 | /** 38 | * vrrp_adv_get_version() - get version_type from received adv pkt 39 | */ 40 | static inline int vrrp_adv_get_version(const struct vrrp_net *vnet) 41 | { 42 | return vnet->__pkt.adv.version_type; 43 | } 44 | 45 | /** 46 | * vrrp_adv_get_priority() - get priority from received adv priority 47 | */ 48 | static inline int vrrp_adv_get_priority(const struct vrrp_net *vnet) 49 | { 50 | return vnet->__pkt.adv.priority; 51 | } 52 | 53 | /** 54 | * vrrp_adv_set_priority() - set priority in emitted adv pkt 55 | */ 56 | static inline void vrrp_adv_set_priority(struct vrrp_net *vnet, uint8_t prio) 57 | { 58 | struct vrrphdr *pkt = vnet->__adv[2].iov_base; 59 | pkt->priority = prio; 60 | 61 | /* recompute chksum */ 62 | pkt->chksum = vnet->adv_checksum(vnet, pkt, NULL, NULL); 63 | 64 | log_notice("vrid %d :: new prio %d applied", vnet->vrid, pkt->priority); 65 | } 66 | 67 | /** 68 | * vrrp_adv_addr_to_str() - return source ip from received adv pkt 69 | * in string format 70 | */ 71 | static inline const char *vrrp_adv_addr_to_str(struct vrrp_net *vnet, char *dst) 72 | { 73 | return vnet->ipx_to_str(&vnet->__pkt.s_ipx, dst); 74 | } 75 | 76 | /** 77 | * vrrp_adv_get_compare() - compare received adv pkt addr to local 78 | * primary address 79 | */ 80 | static inline int vrrp_adv_addr_cmp(struct vrrp_net *vnet) 81 | { 82 | return vnet->ipx_cmp(&vnet->__pkt.s_ipx, &vnet->vif.ipx); 83 | } 84 | 85 | /** 86 | * vrrp_adv_get_advint() - get adv interval 87 | */ 88 | static inline uint16_t vrrp_adv_get_advint(const struct vrrp_net *vnet) 89 | { 90 | if (vnet->__pkt.adv.version_type >> 4 == RFC5798) 91 | return ntohs(vnet->__pkt.adv.max_adv_int); 92 | 93 | return vnet->__pkt.adv.adv_int; 94 | 95 | } 96 | #endif /* _VRRP_ADV_H_ */ 97 | -------------------------------------------------------------------------------- /vrrp_arp.c: -------------------------------------------------------------------------------- 1 | /* 2 | * vrrp_arp.c 3 | * 4 | * Copyright (C) 2014 Arnaud Andre 5 | * 6 | * This file is part of uvrrpd. 7 | * 8 | * uvrrpd is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * uvrrpd is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with uvrrpd. If not, see . 20 | */ 21 | 22 | #include 23 | #include 24 | #include 25 | #include // ETH_P_IP = 0x0800, ETH_P_IPV6 = 0x86DD 26 | #include 27 | #include 28 | #include 29 | 30 | #include "log.h" 31 | #include "vrrp.h" 32 | #include "vrrp_net.h" 33 | 34 | #define ETHDR_SIZE sizeof(struct ether_header) 35 | 36 | /** 37 | * ether_header vrrp_arp_eth 38 | */ 39 | static struct ether_header vrrp_arp_eth = { 40 | .ether_dhost = {0xff, 0xff, 0xff, 41 | 0xff, 0xff, 0xff}, 42 | .ether_shost = {0x00, 43 | 0x00, 44 | 0x5e, 45 | 0x00, 46 | 0x01, 47 | 0x00}, /* vrrp->vrid */ 48 | }; 49 | 50 | 51 | #define IP_ALEN 4 52 | /** 53 | * arphdr_eth - ARP header 54 | */ 55 | struct arphdr_eth { 56 | unsigned char ar_sha[ETH_ALEN]; /* Sender hardware address */ 57 | unsigned char ar_sip[IP_ALEN]; /* Sender IP address */ 58 | unsigned char ar_tha[ETH_ALEN]; /* Target hardware address */ 59 | unsigned char ar_tip[IP_ALEN]; /* Target IP address */ 60 | }; 61 | 62 | 63 | /** 64 | * vrrp_arp_eth_build() 65 | */ 66 | static int vrrp_arp_eth_build(struct iovec *iov, const uint8_t vrid) 67 | { 68 | iov->iov_base = malloc(sizeof(struct ether_header)); 69 | 70 | struct ether_header *hdr = iov->iov_base; 71 | 72 | if (hdr == NULL) { 73 | log_error("vrid %d :: malloc - %m", vrid); 74 | return -1; 75 | } 76 | 77 | memcpy(hdr, &vrrp_arp_eth, sizeof(struct ether_header)); 78 | 79 | hdr->ether_shost[5] = vrid; 80 | hdr->ether_type = htons(ETHERTYPE_ARP); 81 | 82 | iov->iov_len = ETHDR_SIZE; 83 | 84 | return 0; 85 | } 86 | 87 | /** 88 | * vrrp_arp_send() - Send arp gratuitous for each vip 89 | */ 90 | int vrrp_arp_send(struct vrrp_net *vnet) 91 | { 92 | struct vrrp_ip *vip_ptr = NULL; 93 | 94 | /* we have to send one arp pkt by vip */ 95 | list_for_each_entry_reverse(vip_ptr, &vnet->vip_list, iplist) { 96 | vrrp_net_send(vnet, vip_ptr->__topology, 97 | ARRAY_SIZE(vip_ptr->__topology)); 98 | } 99 | 100 | return 0; 101 | } 102 | 103 | /** 104 | * vrrp_arp_build() - Build arp header 105 | */ 106 | static int vrrp_arp_build(struct iovec *iov, const uint8_t vrid) 107 | { 108 | iov->iov_base = malloc(sizeof(struct arphdr)); 109 | 110 | struct arphdr *arph = iov->iov_base; 111 | 112 | if (arph == NULL) { 113 | log_error("vrid %d :: malloc - %m", vrid); 114 | return -1; 115 | } 116 | 117 | arph->ar_hrd = htons(ARPHRD_ETHER); /* Format of hardware address */ 118 | arph->ar_pro = htons(ETHERTYPE_IP); /* Format of protocol address */ 119 | arph->ar_hln = ETH_ALEN; /* Length of hardware address */ 120 | arph->ar_pln = IP_ALEN; /* Length of protocol address */ 121 | arph->ar_op = htons(ARPOP_REQUEST); /* ARP opcode (command) */ 122 | 123 | iov->iov_len = sizeof(struct arphdr); 124 | 125 | return 0; 126 | } 127 | 128 | /** 129 | * vrrp_arp_vrrp_build() - VRRP arp payload 130 | */ 131 | static int vrrp_arp_vrrp_build(struct iovec *iov, struct vrrp_ip *vip, 132 | struct vrrp_net *vnet) 133 | { 134 | iov->iov_base = malloc(sizeof(struct arphdr_eth)); 135 | 136 | struct arphdr_eth *arpeth = iov->iov_base; 137 | 138 | if (arpeth == NULL) { 139 | log_error("vrid %d :: malloc - %m", vnet->vrid); 140 | return -1; 141 | } 142 | 143 | arpeth->ar_sha[0] = 0x00; 144 | arpeth->ar_sha[1] = 0x00; 145 | arpeth->ar_sha[2] = 0x5e; 146 | arpeth->ar_sha[3] = 0x00; 147 | arpeth->ar_sha[4] = 0x01; 148 | arpeth->ar_sha[5] = vnet->vrid; 149 | 150 | memcpy(arpeth->ar_sip, &vip->ip_addr.s_addr, IP_ALEN); 151 | memset(arpeth->ar_tha, 0xFF, ETH_ALEN); 152 | memcpy(arpeth->ar_tip, &arpeth->ar_sip, IP_ALEN); 153 | 154 | iov->iov_len = sizeof(struct arphdr_eth); 155 | 156 | return 0; 157 | } 158 | 159 | /** 160 | * vrrp_arp_init() 161 | */ 162 | int vrrp_arp_init(struct vrrp_net *vnet) 163 | { 164 | int status = -1; 165 | 166 | /* we have to build one arp pkt by vip */ 167 | struct vrrp_ip *vip_ptr = NULL; 168 | 169 | list_for_each_entry_reverse(vip_ptr, &vnet->vip_list, iplist) { 170 | status = 171 | vrrp_arp_eth_build(&vip_ptr->__topology[0], vnet->vrid); 172 | status |= vrrp_arp_build(&vip_ptr->__topology[1], vnet->vrid); 173 | status |= 174 | vrrp_arp_vrrp_build(&vip_ptr->__topology[2], vip_ptr, vnet); 175 | } 176 | 177 | return status; 178 | } 179 | 180 | /** 181 | * vrrp_arp_cleanup() 182 | */ 183 | void vrrp_arp_cleanup(struct vrrp_net *vnet) 184 | { 185 | /* clean arp buffer for each vrrp_ip addr */ 186 | struct vrrp_ip *vip_ptr = NULL; 187 | 188 | list_for_each_entry(vip_ptr, &vnet->vip_list, iplist) { 189 | 190 | /* clean iovec */ 191 | for (int i = 0; i < 3; ++i) { 192 | struct iovec *iov = &vip_ptr->__topology[i]; 193 | free(iov->iov_base); 194 | } 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /vrrp_arp.h: -------------------------------------------------------------------------------- 1 | /* 2 | * vrrp_arp.h - ARP for VRRP IPv4 advertisement 3 | * 4 | * Copyright (C) 2014 Arnaud Andre 5 | * 6 | * This file is part of uvrrpd. 7 | * 8 | * uvrrpd is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * uvrrpd is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with uvrrpd. If not, see . 20 | */ 21 | 22 | #ifndef _VRRP_ARP_H_ 23 | #define _VRRP_ARP_H_ 24 | 25 | int vrrp_arp_init(struct vrrp_net *vnet); 26 | void vrrp_arp_cleanup(struct vrrp_net *vnet); 27 | int vrrp_arp_send(struct vrrp_net *vnet); 28 | 29 | #endif /* _VRRP_ARP_H_ */ 30 | -------------------------------------------------------------------------------- /vrrp_ctrl.c: -------------------------------------------------------------------------------- 1 | /* 2 | * vrrp_ctrl.c - control fifo 3 | * 4 | * Copyright (C) 2016 Arnaud Andre 5 | * 6 | * This file is part of uvrrpd. 7 | * 8 | * uvrrpd is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * uvrrpd is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with uvrrpd. If not, see . 20 | */ 21 | 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include "vrrp.h" 32 | #include "vrrp_ctrl.h" 33 | #include "vrrp_adv.h" 34 | 35 | #include "common.h" 36 | #include "uvrrpd.h" 37 | #include "bits.h" 38 | #include "log.h" 39 | 40 | extern unsigned long reg; 41 | 42 | static inline vrrp_event_t vrrp_ctrl_cmd(struct vrrp *vrrp, 43 | struct vrrp_net *vnet); 44 | 45 | /** 46 | * flush_fifo() - flush a fifo fd 47 | */ 48 | #define BUFLUSH 2048 49 | 50 | static inline int flush_fifo(int fd) 51 | { 52 | ssize_t bytes; 53 | char buf[BUFLUSH]; 54 | 55 | while (1) { 56 | bytes = read(fd, buf, sizeof(buf)); 57 | if (bytes <= 0) { 58 | if (errno == EWOULDBLOCK) { 59 | return 0; 60 | } 61 | else { 62 | log_error("read - %m"); 63 | return -1; 64 | } 65 | } 66 | } 67 | return 0; 68 | } 69 | 70 | /** 71 | * split_cmd() - split a *str in words separated by delim 72 | * Fill a *words_ptr[max_words] with ptr to each found word 73 | * words_ptr must be pre-allocated 74 | * @return nword, number of found word, 75 | * -1 if entry str or words_ptr NULL 76 | */ 77 | static inline int split_cmd(char *str, char **words_ptr, int max_words, 78 | char *delim) 79 | { 80 | if ((str == NULL) || (words_ptr == NULL)) 81 | return -1; 82 | 83 | int nword; 84 | for (nword = 0; nword < max_words; ++nword) { 85 | if (str != NULL) { 86 | while (isspace(*str)) 87 | str++; /* trim whitespace */ 88 | if (str[0] != '\0') 89 | words_ptr[nword] = strsep(&str, delim); 90 | } 91 | 92 | if (words_ptr[nword] == NULL) 93 | break; 94 | } 95 | 96 | 97 | return nword; 98 | } 99 | 100 | /** 101 | * vrrp_ctrl_init() 102 | */ 103 | int vrrp_ctrl_init(struct vrrp_ctrl *ctrl) 104 | { 105 | ctrl->cmd = malloc(sizeof(char *) * CTRL_CMD_NTOKEN); 106 | 107 | if (ctrl->cmd == NULL) { 108 | log_error("init :: malloc - %m"); 109 | return -1; 110 | } 111 | 112 | bzero(ctrl->msg, CTRL_MAXCHAR); 113 | 114 | return 0; 115 | } 116 | 117 | 118 | /** 119 | * vrrp_ctrl_cmd_flush() - Flush cmd 120 | */ 121 | static inline void vrrp_ctrl_cmd_flush(struct vrrp_ctrl *ctrl) 122 | { 123 | if (ctrl == NULL) 124 | return; 125 | 126 | /* clean buff */ 127 | for (int i = 0; i < CTRL_CMD_NTOKEN; ctrl->cmd[i++] = NULL); 128 | bzero(ctrl->msg, CTRL_MAXCHAR); 129 | } 130 | 131 | /** 132 | * vrrp_ctrl_cmd() - Interprete control fifo cmd 133 | */ 134 | static inline vrrp_event_t vrrp_ctrl_cmd(struct vrrp *vrrp, 135 | struct vrrp_net *vnet) 136 | { 137 | int nword; 138 | 139 | nword = 140 | split_cmd(vrrp->ctrl.msg, vrrp->ctrl.cmd, CTRL_CMD_NTOKEN, 141 | WHITESPACE); 142 | 143 | if (nword == 0) 144 | return INVALID; 145 | 146 | /* 147 | * control cmd stop 148 | */ 149 | if (matches(vrrp->ctrl.cmd[0], "stop")) { 150 | log_notice("vrid %d :: control cmd stop, exiting", vrrp->vrid); 151 | set_bit(UVRRPD_RELOAD, ®); 152 | clear_bit(KEEP_GOING, ®); 153 | vrrp_ctrl_cmd_flush(&vrrp->ctrl); 154 | return CTRL_FIFO; 155 | } 156 | 157 | /* 158 | * control cmd reload 159 | */ 160 | if (matches(vrrp->ctrl.cmd[0], "reload")) { 161 | set_bit(UVRRPD_RELOAD, ®); 162 | vrrp_ctrl_cmd_flush(&vrrp->ctrl); 163 | return CTRL_FIFO; 164 | } 165 | 166 | /* 167 | * control cmd state || status 168 | */ 169 | if (matches(vrrp->ctrl.cmd[0], "state") 170 | || matches(vrrp->ctrl.cmd[0], "status")) { 171 | 172 | set_bit(UVRRPD_DUMP, ®); 173 | vrrp_ctrl_cmd_flush(&vrrp->ctrl); 174 | return CTRL_FIFO; 175 | } 176 | 177 | /* 178 | * control cmd prio 179 | */ 180 | if (matches(vrrp->ctrl.cmd[0], "prio")) { 181 | if (nword != 2) { 182 | log_error 183 | ("vrid %d :: invalid syntax, control cmd prio ", 184 | vrrp->vrid); 185 | 186 | vrrp_ctrl_cmd_flush(&vrrp->ctrl); 187 | return INVALID; 188 | } 189 | 190 | /* fetch priority */ 191 | int err; 192 | unsigned long opt; 193 | err = mystrtoul(&opt, vrrp->ctrl.cmd[1], VRRP_PRIO_MAX); 194 | 195 | vrrp_ctrl_cmd_flush(&vrrp->ctrl); 196 | 197 | if (err == -ERANGE) { 198 | log_error 199 | ("vrid %d :: invalid control cmd prio, 0 < priority < 255", 200 | vrrp->vrid); 201 | return INVALID; 202 | } 203 | 204 | if (err == -EINVAL) { 205 | log_error 206 | ("vrid %d :: invalid control cmd prio, error parsing \"%s\" as a number", 207 | vrrp->vrid, vrrp->ctrl.cmd[1]); 208 | return INVALID; 209 | } 210 | 211 | /* change prio */ 212 | if (vrrp->priority != (uint8_t) opt) { 213 | vrrp->priority = (uint8_t) opt; 214 | vrrp_adv_set_priority(vnet, vrrp->priority); 215 | } 216 | 217 | /* reload bit */ 218 | set_bit(UVRRPD_RELOAD, ®); 219 | 220 | return CTRL_FIFO; 221 | } 222 | 223 | vrrp_ctrl_cmd_flush(&vrrp->ctrl); 224 | 225 | return INVALID; 226 | } 227 | 228 | /** 229 | * vrrp_ctrl_read() - Read control fifo 230 | */ 231 | vrrp_event_t vrrp_ctrl_read(struct vrrp * vrrp, struct vrrp_net * vnet) 232 | { 233 | int readbytes = 0; 234 | 235 | readbytes = read(vrrp->ctrl.fd, vrrp->ctrl.msg, CTRL_MAXCHAR); 236 | 237 | if (readbytes > 0) { 238 | flush_fifo(vrrp->ctrl.fd); 239 | vrrp->ctrl.msg[CTRL_MAXCHAR - 1] = '\0'; 240 | return vrrp_ctrl_cmd(vrrp, vnet); 241 | } 242 | 243 | return INVALID; 244 | } 245 | 246 | 247 | /** 248 | * vrrp_ctrl_cleanup() 249 | */ 250 | void vrrp_ctrl_cleanup(struct vrrp_ctrl *ctrl) 251 | { 252 | free(ctrl->cmd); 253 | } 254 | -------------------------------------------------------------------------------- /vrrp_ctrl.h: -------------------------------------------------------------------------------- 1 | /* 2 | * vrrp_ctrl.h - control fifo header 3 | * 4 | * Copyright (C) 2016 Arnaud Andre 5 | * 6 | * This file is part of uvrrpd. 7 | * 8 | * uvrrpd is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * uvrrpd is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with uvrrpd. If not, see . 20 | */ 21 | 22 | #ifndef _VRRP_CTRL_H_ 23 | #define _VRRP_CTRL_H_ 24 | 25 | #include 26 | 27 | /* from vrrp.h */ 28 | struct vrrp; 29 | struct vrrp_net; 30 | typedef enum _vrrp_event_type vrrp_event_t; 31 | 32 | #define CTRL_MAXCHAR 64 33 | #define CTRL_CMD_NTOKEN 3 34 | 35 | /** 36 | * vrrp_ctrl - infos about control fifo 37 | */ 38 | struct vrrp_ctrl { 39 | /* control fifo fd */ 40 | int fd; 41 | 42 | /* control fifo msg */ 43 | char msg[CTRL_MAXCHAR]; 44 | 45 | /* reformated command */ 46 | char **cmd; 47 | }; 48 | 49 | 50 | 51 | int vrrp_ctrl_init(struct vrrp_ctrl *ctrl); 52 | void vrrp_ctrl_cleanup(struct vrrp_ctrl *ctrl); 53 | vrrp_event_t vrrp_ctrl_read(struct vrrp *vrrp, struct vrrp_net *vnet); 54 | 55 | 56 | #endif /* _VRRP_CTRL_H_ */ 57 | -------------------------------------------------------------------------------- /vrrp_exec.c: -------------------------------------------------------------------------------- 1 | /* 2 | * vrrp_exec.c - call an external script while changing state 3 | * 4 | * Copyright (C) 2014 Arnaud Andre 5 | * 6 | * This file is part of uvrrpd. 7 | * 8 | * uvrrpd is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * uvrrpd is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with uvrrpd. If not, see . 20 | */ 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #include "vrrp.h" 33 | #include "vrrp_exec.h" 34 | #include "uvrrpd.h" 35 | #include "common.h" 36 | #include "log.h" 37 | 38 | /* SCRIPT_ARG_MAX : bytes of args 39 | * (255 IPv6 addresses might be specified) 40 | * 1 IPv6 = 45 bytes in a string format 41 | */ 42 | #define SCRIPT_ARG_MAX INET6_ADDRSTRLEN * NI_MAXHOST 43 | #define ADDRSTRLEN INET6_ADDRSTRLEN 44 | #define SCRIPT_NARGS 10 45 | 46 | /** 47 | * vrrp_build_args() - prepare args that will be passed to 48 | * external script 49 | * 50 | * Build a argv array destined to execve() 51 | */ 52 | static int vrrp_build_args(const char *scriptname, char **argv, 53 | const struct vrrp *vrrp, const struct vrrp_net *vnet, 54 | vrrp_state state) 55 | { 56 | /* get basename from scriptname */ 57 | char *name = strchr(scriptname, '/'); 58 | if (name != NULL) { 59 | name++; 60 | snprintf(argv[0], SCRIPT_ARG_MAX, "%s", name); 61 | } 62 | else 63 | snprintf(argv[0], SCRIPT_ARG_MAX, "%s", scriptname); 64 | 65 | /* List of args passed to script : 66 | * 1. state 67 | * 2. vrid 68 | * 3. ifname 69 | * 4. priority 70 | * 5. adv_int 71 | * 6. naddr 72 | * 7. family 73 | * 8 & more. vipaddrs ... 74 | */ 75 | snprintf(argv[1], SCRIPT_ARG_MAX, "%s", STR_STATE(state)); 76 | snprintf(argv[2], SCRIPT_ARG_MAX, "%d", vrrp->vrid); 77 | snprintf(argv[3], SCRIPT_ARG_MAX, "%s", vnet->vif.ifname); 78 | snprintf(argv[4], SCRIPT_ARG_MAX, "%d", vrrp->priority); 79 | snprintf(argv[5], SCRIPT_ARG_MAX, "%d", vrrp->adv_int); 80 | snprintf(argv[6], SCRIPT_ARG_MAX, "%d", vrrp->naddr); 81 | snprintf(argv[7], SCRIPT_ARG_MAX, "%d", 82 | (vnet->family == AF_INET ? 4 : 6)); 83 | 84 | /* serialize vipaddrs 85 | * ip0,ip1,...,ipn */ 86 | int argv_ips = SCRIPT_NARGS - 2; 87 | memset(argv[argv_ips], 0, strlen(argv[argv_ips])); 88 | int plen = 0; 89 | struct vrrp_ip *vip_ptr = NULL; 90 | list_for_each_entry_reverse(vip_ptr, &vnet->vip_list, iplist) { 91 | plen = strlen(argv[argv_ips]); 92 | if (plen != 0) 93 | argv[argv_ips][plen] = ','; 94 | 95 | char straddr[ADDRSTRLEN]; 96 | snprintf(argv[argv_ips] + strlen(argv[argv_ips]), 97 | SCRIPT_ARG_MAX - plen + 1, "%s", 98 | vnet->ipx_to_str(&vip_ptr->ipx, straddr)); 99 | } 100 | 101 | /* the last elmt must be NULL */ 102 | argv[SCRIPT_NARGS - 1] = NULL; 103 | 104 | return 0; 105 | } 106 | 107 | /** 108 | * vrrp_exec() 109 | */ 110 | int vrrp_exec(struct vrrp *vrrp, const struct vrrp_net *vnet, vrrp_state state) 111 | { 112 | const char *scriptname; 113 | 114 | if (vrrp->scriptname == NULL) 115 | scriptname = VRRP_SCRIPT; 116 | else 117 | scriptname = vrrp->scriptname; 118 | 119 | if (!is_file_executable(scriptname)) { 120 | log_error("vrid %d :: File %s doesn't exist or is not executable", 121 | vrrp->vrid, scriptname); 122 | return -1; 123 | } 124 | 125 | vrrp_build_args(scriptname, vrrp->argv, vrrp, vnet, state); 126 | 127 | /* Sig gestion */ 128 | sigset_t blockmask, origmask; 129 | struct sigaction sa_ignore, sa_origquit, sa_origint, sa_default; 130 | 131 | sigemptyset(&blockmask); /* Block SIGCHLD */ 132 | sigaddset(&blockmask, SIGCHLD); 133 | sigprocmask(SIG_BLOCK, &blockmask, &origmask); 134 | sa_ignore.sa_handler = SIG_IGN; /* Ignore SIGINT and SIGQUIT */ 135 | sa_ignore.sa_flags = 0; 136 | sigemptyset(&sa_ignore.sa_mask); 137 | sigaction(SIGINT, &sa_ignore, &sa_origint); 138 | sigaction(SIGQUIT, &sa_ignore, &sa_origquit); 139 | 140 | /* fork */ 141 | uvrrpd_sched_unset(); /* remove SCHED_RR */ 142 | pid_t child = fork(); 143 | int status, savedErrno; 144 | 145 | if (child == -1) { 146 | log_error("vrid %d :: fork - %m", vrrp->vrid); 147 | vrrp_exec_cleanup(vrrp); 148 | return -1; 149 | } 150 | 151 | /* child */ 152 | if (child == 0) { 153 | sa_default.sa_handler = SIG_DFL; 154 | sa_default.sa_flags = 0; 155 | sigemptyset(&sa_default.sa_mask); 156 | if (sa_origint.sa_handler != SIG_IGN) 157 | sigaction(SIGINT, &sa_default, NULL); 158 | if (sa_origquit.sa_handler != SIG_IGN) 159 | sigaction(SIGQUIT, &sa_default, NULL); 160 | sigprocmask(SIG_SETMASK, &origmask, NULL); 161 | 162 | /* execve */ 163 | execve(scriptname, (char *const *) vrrp->argv, NULL); 164 | 165 | log_error("vrid %d :: execve - %m", vrrp->vrid); 166 | vrrp_exec_cleanup(vrrp); 167 | return -1; 168 | } 169 | 170 | /* parent */ 171 | if (child > 0) { 172 | 173 | uvrrpd_sched_set(); /* restore SCHED_RR */ 174 | while (waitpid(child, &status, 0) == -1) { 175 | if (errno != EINTR) { /* Error other than EINTR */ 176 | log_error("vrid %d :: waitpid - %m", vrrp->vrid); 177 | status = -1; 178 | break; /* So exit loop */ 179 | } 180 | } 181 | } 182 | 183 | /* Unblock SIGCHLD, restore dispositions of SIGINT and SIGQUIT */ 184 | savedErrno = errno; /* The following may change 'errno' */ 185 | sigprocmask(SIG_SETMASK, &origmask, NULL); 186 | sigaction(SIGINT, &sa_origint, NULL); 187 | sigaction(SIGQUIT, &sa_origquit, NULL); 188 | errno = savedErrno; 189 | 190 | return status; 191 | } 192 | 193 | /** 194 | * vrrp_exec_init() - init vrrp->argv buffer 195 | */ 196 | int vrrp_exec_init(struct vrrp *vrrp) 197 | { 198 | vrrp->argv = malloc(sizeof(char *) * SCRIPT_NARGS); 199 | 200 | if (vrrp->argv == NULL) { 201 | log_error("vrid %d :: malloc - %m", vrrp->vrid); 202 | return -1; 203 | } 204 | 205 | for (int i = 0; i < SCRIPT_NARGS - 1; ++i) { 206 | vrrp->argv[i] = malloc(sizeof(char) * SCRIPT_ARG_MAX); 207 | if (vrrp->argv[i] == NULL) { 208 | log_error("vrid %d :: malloc - %m", vrrp->vrid); 209 | return -1; 210 | } 211 | bzero(vrrp->argv[i], sizeof(char) * SCRIPT_ARG_MAX); 212 | } 213 | 214 | return 0; 215 | } 216 | 217 | /** 218 | * vrrp_exec_cleanup() - cleanup vrrp->argv buffer 219 | */ 220 | void vrrp_exec_cleanup(struct vrrp *vrrp) 221 | { 222 | if (vrrp->argv != NULL) { 223 | for (int i = 0; i < SCRIPT_NARGS - 1; ++i) { 224 | free(vrrp->argv[i]); 225 | vrrp->argv[i] = NULL; 226 | } 227 | free(vrrp->argv); 228 | vrrp->argv = NULL; 229 | } 230 | } 231 | -------------------------------------------------------------------------------- /vrrp_exec.h: -------------------------------------------------------------------------------- 1 | /* 2 | * vrrp_exec.h - export prototype of vrrp_exec() 3 | * 4 | * Copyright (C) 2014 Arnaud Andre 5 | * 6 | * This file is part of uvrrpd. 7 | * 8 | * uvrrpd is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * uvrrpd is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with uvrrpd. If not, see . 20 | */ 21 | 22 | #ifndef _VRRP_EXEC_H_ 23 | #define _VRRP_EXEC_H_ 24 | 25 | #include "vrrp.h" 26 | 27 | int vrrp_exec(struct vrrp *vrrp, const struct vrrp_net *vnet, vrrp_state state); 28 | int vrrp_exec_init(struct vrrp *vrrp); 29 | void vrrp_exec_cleanup(struct vrrp *vrrp); 30 | 31 | #endif /* _VRRP_EXEC_H_ */ 32 | -------------------------------------------------------------------------------- /vrrp_ip4.c: -------------------------------------------------------------------------------- 1 | /* 2 | * vrrp_ip4.c - IP4 helpers functions 3 | * 4 | * Copyright (C) 2014 Arnaud Andre 5 | * 6 | * This file is part of uvrrpd. 7 | * 8 | * uvrrpd is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * uvrrpd is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with uvrrpd. If not, see . 20 | */ 21 | 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | /* ifreq + ioctl */ 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | #include "vrrp.h" 39 | #include "vrrp_ipx.h" 40 | #include "vrrp_net.h" 41 | #include "vrrp_adv.h" 42 | #include "log.h" 43 | #include "common.h" 44 | 45 | #define VRRP_MGROUP4 "224.0.0.18" 46 | 47 | /** 48 | * pshdr_ip4 - pseudo header IPv4 49 | */ 50 | struct pshdr_ip4 { 51 | uint32_t saddr; 52 | uint32_t daddr; 53 | uint8_t zero; 54 | uint8_t protocol; 55 | uint16_t len; 56 | }; 57 | 58 | /** 59 | * vrrp_ip4_search_vip() - search one vip in vip list 60 | * if vip is found 61 | * set found = 1 62 | * _vip_ptr point to vip in vnet->vip_list 63 | */ 64 | #define vrrp_ip4_search_vip(vnet, _vip_ptr, _addr, found) \ 65 | do { \ 66 | if (_addr != NULL) \ 67 | list_for_each_entry_reverse(_vip_ptr, &vnet->vip_list, iplist) { \ 68 | if ( _vip_ptr->ip_addr.s_addr == *_addr) {\ 69 | found = 1; \ 70 | break; \ 71 | } \ 72 | } \ 73 | } while(0) 74 | 75 | 76 | /** 77 | * vrrp_ip4_mgroup() - join IPv4 VRRP multicast group 78 | */ 79 | static int vrrp_ip4_mgroup(struct vrrp_net *vnet) 80 | { 81 | /* Join VRRP multicast group */ 82 | struct ip_mreq group = { {0}, {0} }; 83 | struct in_addr group_addr = { 0 }; 84 | 85 | if (inet_pton(AF_INET, VRRP_MGROUP4, &group_addr) < 0) { 86 | log_error("vrid %d :: inet_pton - %m", vnet->vrid); 87 | return -1; 88 | } 89 | group.imr_multiaddr.s_addr = group_addr.s_addr; 90 | group.imr_interface.s_addr = vnet->vif.ip_addr.s_addr; 91 | 92 | if (setsockopt 93 | (vnet->socket, IPPROTO_IP, IP_ADD_MEMBERSHIP, &group, 94 | sizeof(struct ip_mreq)) < 0) { 95 | log_error("vrid %d :: setsockopt - %m", vnet->vrid); 96 | return -1; 97 | } 98 | 99 | return 0; 100 | } 101 | 102 | /** 103 | * vrrp_ip4_cmp() - Compare VIP list between received vrrpkt and our instance 104 | * Return 0 if the list is the same, 105 | * the number of different VIP else 106 | */ 107 | static int vrrp_ip4_viplist_cmp(struct vrrp_net *vnet, struct vrrphdr *vrrpkt) 108 | { 109 | /* compare IP address(es) */ 110 | uint32_t *vip_addr = 111 | (uint32_t *) ((unsigned char *) vrrpkt + VRRP_PKTHDR_SIZE); 112 | 113 | uint32_t naddr = 0; 114 | int ndiff = 0; 115 | 116 | for (naddr = 0; naddr < vnet->naddr; ++naddr) { 117 | /* vip in vrrpkt */ 118 | in_addr_t *addr = vip_addr + naddr; 119 | 120 | /* search in vrrp_ip list */ 121 | struct vrrp_ip *vip_ptr = NULL; 122 | int found = 0; 123 | vrrp_ip4_search_vip(vnet, vip_ptr, addr, found); 124 | 125 | if (!found) { 126 | log_warning 127 | ("vrid %d :: Invalid pkt - Virtual IP address unexpected", 128 | vnet->vrid); 129 | ++ndiff; 130 | } 131 | } 132 | return ndiff; 133 | } 134 | 135 | /** 136 | * vrrp_ip4_recv() - Fill vrrp_l3 header from received pkt 137 | */ 138 | static int vrrp_ip4_recv(int sock_fd, struct vrrp_recv *recv, 139 | unsigned char *buf, ssize_t buf_size, int *payload_pos) 140 | { 141 | ssize_t len; 142 | struct iphdr *ip; 143 | 144 | len = read(sock_fd, buf, buf_size); 145 | ip = (struct iphdr *) buf; 146 | 147 | recv->header.len = ip->ihl << 2; 148 | recv->header.proto = ip->protocol; 149 | recv->header.totlen = ntohs(ip->tot_len); 150 | recv->header.ttl = ip->ttl; 151 | 152 | /* saddr and daddr of the ip header */ 153 | recv->ip_saddr.s_addr = ip->saddr; 154 | recv->ip_daddr.s_addr = ip->daddr; 155 | 156 | /* VRRP adv begin here */ 157 | *payload_pos = recv->header.len; 158 | 159 | return len; 160 | } 161 | 162 | /** 163 | * vrrp_ip4_getsize() - return the current size of vrrp instance 164 | */ 165 | static int vrrp_ip4_getsize(const struct vrrp_net *vnet) 166 | { 167 | return sizeof(struct vrrphdr) + vnet->naddr * sizeof(uint32_t) + 168 | VRRP_AUTH_SIZE; 169 | } 170 | 171 | /** 172 | * vrrp_ip4_chksum() - compute vrrp adv chksum 173 | */ 174 | static uint16_t vrrp_ip4_chksum(const struct vrrp_net *vnet, 175 | struct vrrphdr *pkt, 176 | union vrrp_ipx_addr *ipx_saddr, 177 | union vrrp_ipx_addr *ipx_daddr) 178 | { 179 | /* reset chksum */ 180 | pkt->chksum = 0; 181 | 182 | if ((pkt->version_type >> 4) == RFC3768) 183 | return cksum((unsigned short *) pkt, vrrp_ip4_getsize(vnet)); 184 | 185 | if ((pkt->version_type >> 4) == RFC5798) { 186 | const struct iovec *iov_iph = &vnet->__adv[1]; 187 | const struct iphdr *iph = iov_iph->iov_base; 188 | 189 | /* get saddr and daddr */ 190 | uint32_t saddr = 0; 191 | uint32_t daddr = 0; 192 | 193 | if (ipx_saddr != NULL) 194 | saddr = ipx_saddr->addr.s_addr; 195 | if (ipx_daddr != NULL) 196 | daddr = ipx_daddr->addr.s_addr; 197 | 198 | /* pseudo_header ipv4 */ 199 | struct pshdr_ip4 psh = { 0 }; 200 | 201 | /* if saddr and daddr are not specified, use addresses from iphdr */ 202 | psh.saddr = (saddr ? saddr : iph->saddr); 203 | psh.daddr = (daddr ? daddr : iph->daddr); 204 | psh.zero = 0; 205 | psh.protocol = iph->protocol; 206 | psh.len = htons(vrrp_ip4_getsize(vnet)); 207 | 208 | uint32_t psh_size = 209 | sizeof(struct pshdr_ip4) + vrrp_ip4_getsize(vnet); 210 | 211 | unsigned short buf[psh_size / sizeof(short)]; 212 | 213 | memcpy(buf, &psh, sizeof(struct pshdr_ip4)); 214 | memcpy(buf + sizeof(struct pshdr_ip4) / sizeof(short), pkt, 215 | vrrp_ip4_getsize(vnet)); 216 | 217 | return cksum(buf, psh_size); 218 | } 219 | 220 | return 0; 221 | } 222 | 223 | /** 224 | * vrrp_ip4_ntop() - network to string representation 225 | */ 226 | static const char *vrrp_ip4_ntop(union vrrp_ipx_addr *ipx, char *dst) 227 | { 228 | return inet_ntop(AF_INET, &ipx->addr, dst, INET_ADDRSTRLEN); 229 | } 230 | 231 | /** 232 | * vrrp_ip4_pton() - string representation to network 233 | */ 234 | static int vrrp_ip4_pton(union vrrp_ipx_addr *dst, const char *src) 235 | { 236 | return inet_pton(AF_INET, src, &dst->addr); 237 | } 238 | 239 | 240 | /** 241 | * vrrp_ip4_cmp() - compare two vipx 242 | */ 243 | int vrrp_ip4_cmp(union vrrp_ipx_addr *s1, union vrrp_ipx_addr *s2) 244 | { 245 | return ntohl(s1->addr.s_addr) - ntohl(s2->addr.s_addr); 246 | } 247 | 248 | /** 249 | * vrrp_ip4_setsockopt() - no need to setsockopt() in IPv4 250 | */ 251 | static int vrrp_ip4_setsockopt( __attribute__ ((unused)) 252 | int sock_fd, __attribute__ ((unused)) 253 | int vrid) 254 | { 255 | /* nop */ 256 | return 0; 257 | } 258 | 259 | /* exported VRRP_IP4 helper */ 260 | struct vrrp_ipx VRRP_IP4 = { 261 | .family = AF_INET, 262 | .setsockopt = vrrp_ip4_setsockopt, 263 | .mgroup = vrrp_ip4_mgroup, 264 | .recv = vrrp_ip4_recv, 265 | .cmp = vrrp_ip4_cmp, 266 | .chksum = vrrp_ip4_chksum, 267 | .viplist_cmp = vrrp_ip4_viplist_cmp, 268 | .getsize = vrrp_ip4_getsize, 269 | .ipx_pton = vrrp_ip4_pton, 270 | .ipx_ntop = vrrp_ip4_ntop, 271 | }; 272 | -------------------------------------------------------------------------------- /vrrp_ip6.c: -------------------------------------------------------------------------------- 1 | /* 2 | * vrrp_ip6.c - IPv6 helpers functions 3 | * 4 | * Copyright (C) 2014 Arnaud Andre 5 | * 6 | * This file is part of uvrrpd. 7 | * 8 | * uvrrpd is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * uvrrpd is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with uvrrpd. If not, see . 20 | */ 21 | 22 | #ifdef HAVE_IP6 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | /* ifreq + ioctl */ 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include /* NI_MAXHOST */ 39 | 40 | #include "vrrp_ipx.h" 41 | #include "vrrp_net.h" 42 | #include "log.h" 43 | #include "common.h" 44 | 45 | #define VRRP_MGROUP6 "ff02::12" 46 | #define IP6HDR_SIZE sizeof(struct ip6_hdr) 47 | 48 | /** 49 | * pshdr_ip6 - pseudo header IPv6 50 | */ 51 | struct pshdr_ip6 { 52 | struct in6_addr saddr; 53 | struct in6_addr daddr; 54 | uint32_t len; 55 | uint8_t zeros[3]; 56 | uint8_t next_header; 57 | }; 58 | 59 | /** 60 | * vrrp_ip6_search_vip() - search one vip in vip list 61 | * if vip is found 62 | * set found = 1 63 | * _vip_ptr point to vip in vnet->vip_list 64 | */ 65 | #define vrrp_ip6_search_vip(vnet, _vip_ptr, _addr, found) \ 66 | do { \ 67 | list_for_each_entry_reverse(_vip_ptr, &vnet->vip_list, iplist) { \ 68 | if (memcmp(&(_vip_ptr->ip_addr6), _addr, sizeof(struct in6_addr)) == 0) { \ 69 | found = 1; \ 70 | break; \ 71 | }\ 72 | } \ 73 | } while(0) 74 | 75 | /** 76 | * vrrp_ip6_setsockopt() - Set socket option 77 | * used to find ancillary data in recvmsg() 78 | * see vrrp_ip6_recv() 79 | */ 80 | static int vrrp_ip6_setsockopt(int socket, int vrid) 81 | { 82 | int on = 1; 83 | 84 | /* IPV6_RECVPKTINFO */ 85 | if (setsockopt 86 | (socket, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, sizeof(on)) < 0) { 87 | log_error("vrid %d :: setsockopt - %m", vrid); 88 | return -1; 89 | } 90 | 91 | /* IPV6_RECVHOPLIMIT */ 92 | if (setsockopt 93 | (socket, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on, sizeof(on)) < 0) { 94 | log_error("vrid %d :: setsockopt - %m", vrid); 95 | return -1; 96 | } 97 | 98 | return 0; 99 | } 100 | 101 | /** 102 | * vrrp_net_join_mgroup6() - join IPv6 VRRP multicast group 103 | */ 104 | static int vrrp_ip6_mgroup(struct vrrp_net *vnet) 105 | { 106 | /* Join VRRP multicast group */ 107 | struct ipv6_mreq group = { IN6ADDR_ANY_INIT, 0 }; 108 | struct in6_addr group_addr = IN6ADDR_ANY_INIT; 109 | 110 | if (inet_pton(AF_INET6, VRRP_MGROUP6, &group_addr) < 0) { 111 | log_error("vrid %d :: inet_pton - %m", vnet->vrid); 112 | return -1; 113 | } 114 | 115 | memcpy(&group.ipv6mr_multiaddr, &group_addr, sizeof(struct in6_addr)); 116 | 117 | group.ipv6mr_interface = if_nametoindex(vnet->vif.ifname); 118 | 119 | if (setsockopt 120 | (vnet->socket, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &group, 121 | sizeof(struct ipv6_mreq)) < 0) { 122 | log_error("vrid %d :: setsockopt - %m", vnet->vrid); 123 | return -1; 124 | } 125 | 126 | return 0; 127 | } 128 | 129 | /** 130 | * vrrp_ip6_cmp() - Compare VIP list between received vrrpkt and our instance 131 | * Return 0 if the list is the same, 132 | * the number of differente VIP else 133 | */ 134 | static int vrrp_ip6_viplist_cmp(struct vrrp_net *vnet, struct vrrphdr *vrrpkt) 135 | { 136 | uint32_t *vip_addr = 137 | (uint32_t *) ((unsigned char *) vrrpkt + VRRP_PKTHDR_SIZE); 138 | 139 | uint32_t pos = 0; 140 | int naddr = 0; 141 | int ndiff = 0; 142 | 143 | while (naddr < vnet->naddr) { 144 | /* vip in vrrpkt */ 145 | struct in6_addr *vip = (struct in6_addr *) (vip_addr + pos); 146 | 147 | /* search in vrrp_ip list */ 148 | struct vrrp_ip *vip_ptr = NULL; 149 | int found = 0; 150 | 151 | vrrp_ip6_search_vip(vnet, vip_ptr, vip->s6_addr, found); 152 | 153 | if (!found) { 154 | char host[NI_MAXHOST]; 155 | log_warning 156 | ("vrid %d :: Invalid pkt - Virtual IPv6 address unexpected %s", 157 | vnet->vrid, inet_ntop(vnet->family, vip, host, 158 | sizeof(host))); 159 | ++ndiff; 160 | } 161 | pos += 4; 162 | ++naddr; 163 | } 164 | return ndiff; 165 | } 166 | 167 | /** 168 | * find_ancillary() - search & find IPv6 ancillary data after 169 | * receiving data in a struct msghdr msg (recvmsg()) 170 | */ 171 | static inline void *find_ancillary(struct msghdr *msg, int cmsg_type) 172 | { 173 | struct cmsghdr *cmsg = NULL; 174 | 175 | for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL; 176 | cmsg = CMSG_NXTHDR(msg, cmsg)) { 177 | if ((cmsg->cmsg_level == IPPROTO_IPV6) 178 | && (cmsg->cmsg_type == cmsg_type)) { 179 | return (CMSG_DATA(cmsg)); 180 | } 181 | } 182 | 183 | return NULL; 184 | } 185 | 186 | /** 187 | * vrrp_ip6_recv() - Fill vrrp_ipx_header from received pkt 188 | */ 189 | static int vrrp_ip6_recv(int sock_fd, struct vrrp_recv *recv, 190 | unsigned char *buf, ssize_t buf_size, int *payload_pos) 191 | { 192 | ssize_t len; 193 | 194 | /* IPv6 raw sockets return no IP header. We must query 195 | * src/dest via socket options/ancillary data */ 196 | struct msghdr msg; 197 | struct sockaddr_in6 src; 198 | struct iovec iov; 199 | uint8_t ancillary[64]; 200 | 201 | msg.msg_name = &src; 202 | msg.msg_namelen = sizeof(src); 203 | iov.iov_base = buf; 204 | iov.iov_len = buf_size; 205 | msg.msg_iov = &iov; 206 | msg.msg_iovlen = 1; 207 | msg.msg_control = ancillary; 208 | msg.msg_controllen = sizeof(ancillary); 209 | msg.msg_flags = 0; 210 | 211 | len = recvmsg(sock_fd, &msg, 0); 212 | 213 | if (len < 0) { 214 | log_error("recvmsg - %m"); 215 | return -1; 216 | } 217 | 218 | /* src address */ 219 | memcpy(&recv->ip_saddr6, &src.sin6_addr, sizeof(struct in6_addr)); 220 | 221 | /* hoplimit */ 222 | uint8_t *opt = find_ancillary(&msg, IPV6_HOPLIMIT); 223 | if (opt == NULL) { 224 | log_error("recvmsg - unknown hop limit"); 225 | return -1; 226 | } 227 | 228 | recv->header.ttl = *(int *) opt; 229 | 230 | /* dst address */ 231 | opt = find_ancillary(&msg, IPV6_PKTINFO); 232 | if (opt == NULL) { 233 | log_error("recvmsg - unknown dst address"); 234 | return -1; 235 | } 236 | 237 | struct in6_pktinfo *pktinfo = (struct in6_pktinfo *) opt; 238 | 239 | memcpy(&recv->ip_daddr6, &pktinfo->ipi6_addr, sizeof(struct in6_addr)); 240 | 241 | /* other options */ 242 | recv->header.len = sizeof(struct ip6_hdr); 243 | recv->header.totlen = recv->header.len + len; 244 | /* kludge, we force it since we have no way to read it in recvmsg(). 245 | * But we have set IPPROTO_VRRP in vrrp_net_socket() */ 246 | recv->header.proto = IPPROTO_VRRP; 247 | 248 | /* buf is directly filled with VRRP adv 249 | * no need to skip IPv6 header */ 250 | *payload_pos = 0; 251 | 252 | return len; 253 | } 254 | 255 | /** 256 | * vrrp_ip6_getsize() - return the current size of vrrp instance 257 | */ 258 | static int vrrp_ip6_getsize(const struct vrrp_net *vnet) 259 | { 260 | return sizeof(struct vrrphdr) + vnet->naddr * sizeof(struct in6_addr); 261 | } 262 | 263 | /** 264 | * vrrp_ip6_chksum() - compute VRRP adv chksum 265 | */ 266 | uint16_t vrrp_ip6_chksum(const struct vrrp_net *vnet, struct vrrphdr *pkt, 267 | union vrrp_ipx_addr *ipx_saddr, 268 | union vrrp_ipx_addr *ipx_daddr) 269 | { 270 | /* reset chksum */ 271 | pkt->chksum = 0; 272 | 273 | const struct iovec *iov_iph = &vnet->__adv[1]; 274 | const struct ip6_hdr *iph = iov_iph->iov_base; 275 | 276 | /* pseudo_header ipv6 */ 277 | struct pshdr_ip6 psh = { IN6ADDR_ANY_INIT, 278 | IN6ADDR_ANY_INIT, 279 | 0, 280 | {0, 0, 0}, 281 | 0 282 | }; 283 | 284 | if ((ipx_saddr != NULL) && (ipx_daddr != NULL)) { 285 | memcpy(&psh.saddr, &ipx_saddr->addr6, sizeof(struct in6_addr)); 286 | memcpy(&psh.daddr, &ipx_daddr->addr6, sizeof(struct in6_addr)); 287 | } 288 | else { 289 | memcpy(&psh.saddr, &iph->ip6_src, sizeof(struct in6_addr)); 290 | memcpy(&psh.daddr, &iph->ip6_dst, sizeof(struct in6_addr)); 291 | } 292 | 293 | 294 | bzero(&psh.zeros, sizeof(psh.zeros)); 295 | psh.next_header = IPPROTO_VRRP; 296 | psh.len = htons(vrrp_ip6_getsize(vnet)); 297 | 298 | uint32_t psh_size = sizeof(struct pshdr_ip6) + vrrp_ip6_getsize(vnet); 299 | unsigned short buf[psh_size / sizeof(short)]; 300 | 301 | memcpy(buf, &psh, sizeof(struct pshdr_ip6)); 302 | memcpy(buf + sizeof(struct pshdr_ip6) / sizeof(short), pkt, 303 | vrrp_ip6_getsize(vnet)); 304 | 305 | return cksum(buf, psh_size); 306 | } 307 | 308 | /** 309 | * vrrp_ip6_ntop() - network to string representation 310 | */ 311 | static const char *vrrp_ip6_ntop(union vrrp_ipx_addr *ipx, char *dst) 312 | { 313 | return inet_ntop(AF_INET6, &ipx->addr6, dst, INET6_ADDRSTRLEN); 314 | } 315 | 316 | /** 317 | * vrrp_ip6_pton() - string representation to network 318 | */ 319 | static int vrrp_ip6_pton(union vrrp_ipx_addr *dst, const char *src) 320 | { 321 | return inet_pton(AF_INET6, src, &dst->addr6); 322 | } 323 | 324 | /** 325 | * vrrp_ip6_cmp() - compare two vipx 326 | */ 327 | int vrrp_ip6_cmp(union vrrp_ipx_addr *s1, union vrrp_ipx_addr *s2) 328 | { 329 | return memcmp(&s1->addr6, &s2->addr6, sizeof(struct in6_addr)); 330 | } 331 | 332 | /* exported VRRP_IP6 helper */ 333 | struct vrrp_ipx VRRP_IP6 = { 334 | .family = AF_INET6, 335 | .setsockopt = vrrp_ip6_setsockopt, 336 | .mgroup = vrrp_ip6_mgroup, 337 | .recv = vrrp_ip6_recv, 338 | .cmp = vrrp_ip6_cmp, 339 | .chksum = vrrp_ip6_chksum, 340 | .getsize = vrrp_ip6_getsize, 341 | .viplist_cmp = vrrp_ip6_viplist_cmp, 342 | .ipx_pton = vrrp_ip6_pton, 343 | .ipx_ntop = vrrp_ip6_ntop 344 | }; 345 | 346 | #endif /* HAVE_IP6 */ 347 | -------------------------------------------------------------------------------- /vrrp_ipx.h: -------------------------------------------------------------------------------- 1 | /** 2 | * vrrp_ipx.h 3 | * 4 | * Copyright (C) 2014 Arnaud Andre 5 | * 6 | * This file is part of uvrrpd. 7 | * 8 | * uvrrpd is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * uvrrpd is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with uvrrpd. If not, see . 20 | */ 21 | 22 | #ifndef _VRRP_IPX_ 23 | #define _VRRP_IPX_ 24 | 25 | 26 | /* from vrrp_net.h */ 27 | struct vrrp_net; 28 | struct vrrphdr; 29 | struct vrrp_recv; 30 | 31 | /** 32 | * struct vrrp_ipx_header - IPvX header informations from received adv pkt 33 | */ 34 | struct vrrp_ipx_header { 35 | int len; 36 | int proto; 37 | int totlen; 38 | int ttl; 39 | }; 40 | 41 | /** 42 | * union vrrp_ipx_addr - IPv4 and IPv6 addr struct 43 | */ 44 | union vrrp_ipx_addr { 45 | struct in_addr addr; 46 | #ifdef HAVE_IP6 47 | struct in6_addr addr6; 48 | #endif 49 | }; 50 | 51 | /** 52 | * struct vrrp_ipx - Helper functions 53 | */ 54 | struct vrrp_ipx { 55 | /* AF_INET or AF6_INET */ 56 | int family; 57 | 58 | /* setsockopt() - socket options */ 59 | int (*setsockopt) (int, int); 60 | 61 | /* mgroup() - join IPvX VRRP multicast group */ 62 | int (*mgroup) (struct vrrp_net *); 63 | 64 | /* cmp() - Compare two IPvX address */ 65 | int (*cmp) (union vrrp_ipx_addr *, union vrrp_ipx_addr *); 66 | 67 | /* recv() - Read received pkt and fetch/store information 68 | * in struct vrrp_recv */ 69 | int (*recv) (int, struct vrrp_recv *, unsigned char *, ssize_t, int *); 70 | 71 | /* getsize() - get size of IPvX VRRP advertisement pkt */ 72 | int (*getsize) (const struct vrrp_net *); 73 | 74 | /* viplist_cmp() - compare VRRP Virtual IPvX list */ 75 | int (*viplist_cmp) (struct vrrp_net *, struct vrrphdr *); 76 | 77 | /* chksum() - compute IPvX checksum while building 78 | * advertisement packet */ 79 | uint16_t(*chksum) (const struct vrrp_net *, struct vrrphdr *, 80 | union vrrp_ipx_addr *, union vrrp_ipx_addr *); 81 | 82 | /* ipx_ntop() - call ntop() on IPvX addr */ 83 | const char *(*ipx_ntop) (union vrrp_ipx_addr *, char *); 84 | 85 | /* ipx_pton() - call pton() on IPvX addr */ 86 | int (*ipx_pton) (union vrrp_ipx_addr *, const char *); 87 | }; 88 | 89 | /* IP4 and IP6 internal modules */ 90 | extern struct vrrp_ipx VRRP_IP4; /* IPv4 module */ 91 | #ifdef HAVE_IP6 92 | extern struct vrrp_ipx VRRP_IP6; /* IPv6 module */ 93 | #endif 94 | 95 | /** 96 | * vrrp_ipx_set() - Set l3 helper for vrrp_net 97 | */ 98 | static inline struct vrrp_ipx *vrrp_ipx_set(int family) 99 | { 100 | if (family == AF_INET) { 101 | return &VRRP_IP4; 102 | } 103 | #ifdef HAVE_IP6 104 | if (family == AF_INET6) { 105 | return &VRRP_IP6; 106 | } 107 | #endif 108 | return NULL; 109 | } 110 | 111 | #endif /* _VRRP_IPX_ */ 112 | -------------------------------------------------------------------------------- /vrrp_na.c: -------------------------------------------------------------------------------- 1 | /* 2 | * vrrp_na.c - Neighbour Advertisement 3 | * 4 | * TODO : need work to use chksum helper in vrrp_ip6.c 5 | * 6 | * Copyright (C) 2014 Arnaud Andre 7 | * 8 | * This file is part of uvrrpd. 9 | * 10 | * uvrrpd is free software: you can redistribute it and/or modify 11 | * it under the terms of the GNU General Public License as published by 12 | * the Free Software Foundation, either version 3 of the License, or 13 | * (at your option) any later version. 14 | * 15 | * uvrrpd is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU General Public License for more details. 19 | * 20 | * You should have received a copy of the GNU General Public License 21 | * along with uvrrpd. If not, see . 22 | */ 23 | 24 | #ifdef HAVE_IP6 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include // struct nd_neighbor_advert 33 | 34 | #include "log.h" 35 | #include "vrrp.h" 36 | #include "vrrp_net.h" 37 | 38 | #define IN6ADDR_MCAST "ff02::1" 39 | 40 | #define ETHDR_SIZE sizeof(struct ether_header) 41 | 42 | /** 43 | * ether_header vrrp_na_eth 44 | */ 45 | static struct ether_header vrrp_na_eth = { 46 | .ether_dhost = {0x33, 0x33, 0x00, 47 | 0x00, 0x00, 0x01}, 48 | .ether_shost = {0x00, 49 | 0x00, 50 | 0x52, 51 | 0x00, 52 | 0x01, 53 | 0x00}, /* vrrp->vrid */ 54 | }; 55 | 56 | /** 57 | * pshdr_ip6 - pseudo header IPv6 58 | */ 59 | struct pshdr_ip6 { 60 | struct in6_addr saddr; 61 | struct in6_addr daddr; 62 | uint32_t len; 63 | uint8_t zeros[3]; 64 | uint8_t next_header; 65 | }; 66 | 67 | /** 68 | * vrrp_na_eth_build() 69 | */ 70 | static int vrrp_na_eth_build(struct iovec *iov, const uint8_t vrid) 71 | { 72 | iov->iov_base = malloc(sizeof(struct ether_header)); 73 | 74 | struct ether_header *hdr = iov->iov_base; 75 | 76 | if (hdr == NULL) { 77 | log_error("vrid %d :: malloc - %m", vrid); 78 | return -1; 79 | } 80 | 81 | memcpy(hdr, &vrrp_na_eth, sizeof(struct ether_header)); 82 | 83 | hdr->ether_shost[5] = vrid; 84 | hdr->ether_type = htons(ETH_P_IPV6); 85 | 86 | iov->iov_len = ETHDR_SIZE; 87 | 88 | return 0; 89 | } 90 | 91 | /** 92 | * vrrp_na_ip6_build() 93 | */ 94 | static int vrrp_na_ip6_build(struct iovec *iov, struct vrrp_ip *ip, 95 | const struct vrrp_net *vnet) 96 | { 97 | iov->iov_base = malloc(sizeof(struct ip6_hdr)); 98 | 99 | struct ip6_hdr *ip6h = iov->iov_base; 100 | 101 | if (ip6h == NULL) { 102 | log_error("vrid %d :: malloc - %m", vnet->vrid); 103 | return -1; 104 | } 105 | 106 | ip6h->ip6_flow = htonl((6 << 28) | (0 << 20) | 0); 107 | ip6h->ip6_plen = htons(sizeof(struct nd_neighbor_advert)); 108 | ip6h->ip6_nxt = IPPROTO_ICMPV6; 109 | ip6h->ip6_hlim = 0xff; 110 | 111 | memcpy(&ip6h->ip6_src, &ip->ip_addr6, sizeof(struct in6_addr)); 112 | 113 | if (inet_pton(AF_INET6, IN6ADDR_MCAST, &(ip6h->ip6_dst)) != 1) { 114 | log_error("vrid %d :: inet_pton - %m", vnet->vrid); 115 | return -1; 116 | } 117 | 118 | iov->iov_len = sizeof(struct ip6_hdr); 119 | 120 | return 0; 121 | } 122 | 123 | 124 | /** 125 | * vrrp_na_chksum() - compute na chksum 126 | */ 127 | uint16_t vrrp_na_chksum(struct iovec * iov, struct nd_neighbor_advert * na) 128 | { 129 | /* reset chksum */ 130 | na->nd_na_hdr.icmp6_cksum = 0; 131 | 132 | const struct ip6_hdr *iph = iov->iov_base; 133 | 134 | /* pseudo_header ipv6 */ 135 | struct pshdr_ip6 psh = { IN6ADDR_ANY_INIT, /* saddr */ 136 | IN6ADDR_ANY_INIT, /* daddr */ 137 | 0, /* length */ 138 | {0, 0, 0}, /* zeros[3] */ 139 | 0 /* next_header */ 140 | }; 141 | 142 | memcpy(&psh.saddr, &iph->ip6_src, sizeof(struct in6_addr)); 143 | memcpy(&psh.daddr, &iph->ip6_dst, sizeof(struct in6_addr)); 144 | 145 | bzero(&psh.zeros, sizeof(psh.zeros)); 146 | psh.next_header = IPPROTO_ICMPV6; 147 | psh.len = htons(sizeof(struct nd_neighbor_advert)); 148 | 149 | uint32_t psh_size = 150 | sizeof(struct pshdr_ip6) + sizeof(struct nd_neighbor_advert); 151 | unsigned short buf[psh_size / sizeof(short)]; 152 | 153 | memcpy(buf, &psh, sizeof(struct pshdr_ip6)); 154 | memcpy(buf + sizeof(struct pshdr_ip6) / sizeof(short), na, 155 | sizeof(struct nd_neighbor_advert)); 156 | 157 | return cksum(buf, psh_size); 158 | } 159 | 160 | /** 161 | * vrrp_na_build() 162 | */ 163 | static int vrrp_na_build(struct iovec *iov, struct vrrp_ip *ip, 164 | const struct vrrp_net *vnet) 165 | { 166 | iov->iov_base = malloc(sizeof(struct nd_neighbor_advert)); 167 | 168 | struct nd_neighbor_advert *na = iov->iov_base; 169 | 170 | if (na == NULL) { 171 | log_error("vrid %d :: malloc - %m", vnet->vrid); 172 | return -1; 173 | } 174 | 175 | na->nd_na_hdr.icmp6_type = ND_NEIGHBOR_ADVERT; 176 | na->nd_na_hdr.icmp6_code = 0; 177 | na->nd_na_hdr.icmp6_cksum = 0; 178 | /* Set R/S/O flags as = R=1, S=0, O=1 (RFC 5798, 6.4.2.(395)) */ 179 | na->nd_na_flags_reserved = ND_NA_FLAG_ROUTER | ND_NA_FLAG_OVERRIDE; 180 | memcpy(&na->nd_na_target, &ip->ip_addr6, sizeof(struct in6_addr)); 181 | 182 | na->nd_na_hdr.icmp6_cksum = vrrp_na_chksum(&ip->__topology[1], na); 183 | 184 | iov->iov_len = sizeof(struct nd_neighbor_advert); 185 | 186 | return 0; 187 | } 188 | 189 | /** 190 | * vrrp_na_send() - for each vip send an unsollicited neighbor advertisement 191 | */ 192 | int vrrp_na_send(struct vrrp_net *vnet) 193 | { 194 | struct vrrp_ip *vip_ptr = NULL; 195 | 196 | /* we have to send one na by vip */ 197 | list_for_each_entry_reverse(vip_ptr, &vnet->vip_list, iplist) { 198 | vrrp_net_send(vnet, vip_ptr->__topology, 199 | ARRAY_SIZE(vip_ptr->__topology)); 200 | } 201 | 202 | return 0; 203 | } 204 | 205 | /** 206 | * vrrp_na_init() 207 | */ 208 | int vrrp_na_init(struct vrrp_net *vnet) 209 | { 210 | int status = -1; 211 | 212 | /* we have to build one na pkt by vip */ 213 | struct vrrp_ip *vip_ptr = NULL; 214 | 215 | list_for_each_entry_reverse(vip_ptr, &vnet->vip_list, iplist) { 216 | status = vrrp_na_eth_build(&vip_ptr->__topology[0], vnet->vrid); 217 | status |= 218 | vrrp_na_ip6_build(&vip_ptr->__topology[1], vip_ptr, vnet); 219 | status |= vrrp_na_build(&vip_ptr->__topology[2], vip_ptr, vnet); 220 | } 221 | 222 | return status; 223 | } 224 | 225 | 226 | /** 227 | * vrrp_na_cleanup() 228 | */ 229 | void vrrp_na_cleanup(struct vrrp_net *vnet) 230 | { 231 | /* clean na buffer for each vrrp_ip addr */ 232 | struct vrrp_ip *vip_ptr = NULL; 233 | 234 | list_for_each_entry(vip_ptr, &vnet->vip_list, iplist) { 235 | 236 | /* clean iovec */ 237 | for (int i = 0; i < 3; ++i) { 238 | struct iovec *iov = &vip_ptr->__topology[i]; 239 | free(iov->iov_base); 240 | } 241 | 242 | } 243 | } 244 | 245 | #endif /* HAVE_IP6 */ 246 | -------------------------------------------------------------------------------- /vrrp_na.h: -------------------------------------------------------------------------------- 1 | /* 2 | * vrrp_na.h - Neighbour Advertisement 3 | * 4 | * Copyright (C) 2014 Arnaud Andre 5 | * 6 | * This file is part of uvrrpd. 7 | * 8 | * uvrrpd is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * uvrrpd is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with uvrrpd. If not, see . 20 | */ 21 | 22 | #ifdef HAVE_IP6 23 | 24 | #ifndef _VRRP_NA_H_ 25 | #define _VRRP_NA_H_ 26 | 27 | int vrrp_na_init(struct vrrp_net *vnet); 28 | void vrrp_na_cleanup(struct vrrp_net *vnet); 29 | int vrrp_na_send(struct vrrp_net *vnet); 30 | 31 | #endif /* _VRRP_NA_H_ */ 32 | 33 | #endif /* HAVE_IP6 */ 34 | -------------------------------------------------------------------------------- /vrrp_net.c: -------------------------------------------------------------------------------- 1 | /* 2 | * vrrp_net.c - net layer 3 | * 4 | * Copyright (C) 2014 Arnaud Andre 5 | * 6 | * This file is part of uvrrpd. 7 | * 8 | * uvrrpd is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * uvrrpd is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with uvrrpd. If not, see . 20 | */ 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | /* ifreq + ioctl */ 33 | #include 34 | #include 35 | 36 | #include 37 | #include 38 | #include /* NI_MAXHOST */ 39 | 40 | #include "vrrp.h" 41 | #include "vrrp_net.h" 42 | #include "vrrp_adv.h" 43 | 44 | #include "common.h" 45 | #include "list.h" 46 | #include "log.h" 47 | 48 | #include "linux/types.h" 49 | 50 | #define VRRP_TTL 255 51 | 52 | static inline void vrrp_net_invalidate_buffer(struct vrrp_net *vnet); 53 | 54 | /** 55 | * vrrp_net_init() - init struct vrrp_net of vrrp instance 56 | */ 57 | void vrrp_net_init(struct vrrp_net *vnet) 58 | { 59 | vnet->vrid = 0; 60 | vnet->naddr = 0; 61 | vnet->socket = 0; 62 | vnet->xmit = 0; 63 | vnet->family = AF_INET; 64 | vnet->ipx_helper = NULL; 65 | 66 | /* init VRRP IPs list */ 67 | INIT_LIST_HEAD(&vnet->vip_list); 68 | 69 | /* init vrrp interface */ 70 | bzero((void *) &vnet->vif, sizeof(struct vrrp_if)); 71 | 72 | /* init pkt buffer */ 73 | bzero((void *) &vnet->__pkt, sizeof(struct vrrp_recv)); 74 | } 75 | 76 | /** 77 | * vrrp_net_cleanup() - cleanup struct vrrp_net 78 | */ 79 | void vrrp_net_cleanup(struct vrrp_net *vnet) 80 | { 81 | /* clean VIP addr */ 82 | struct vrrp_ip *vip_ptr = NULL; 83 | struct vrrp_ip *n = NULL; 84 | list_for_each_entry_safe(vip_ptr, n, &vnet->vip_list, iplist) 85 | free(vip_ptr); 86 | 87 | free(vnet->vif.ifname); 88 | 89 | /* close sockets */ 90 | close(vnet->socket); 91 | close(vnet->xmit); 92 | } 93 | 94 | /** 95 | * vrrp_net_socket() - create VRRP socket destined to receive VRRP pkt 96 | */ 97 | int vrrp_net_socket(struct vrrp_net *vnet) 98 | { 99 | /* Open RAW socket */ 100 | vnet->socket = socket(vnet->family, SOCK_RAW, IPPROTO_VRRP); 101 | 102 | if (vnet->socket < 0) { 103 | log_error("vrid %d :: socket - %m", vnet->vrid); 104 | return -1; 105 | } 106 | 107 | vnet->ipx_helper = vrrp_ipx_set(vnet->family); 108 | 109 | if (vnet->ipx_helper == NULL) { 110 | log_error("family not valid"); 111 | return -1; 112 | } 113 | 114 | int status = -1; 115 | 116 | status = vnet->set_sockopt(vnet->socket, vnet->vrid); 117 | status |= vnet->join_mgroup(vnet); 118 | 119 | return status; 120 | } 121 | 122 | /** 123 | * vrrp_net_socket_xmit() - open raw VRRP xmit socket 124 | */ 125 | int vrrp_net_socket_xmit(struct vrrp_net *vnet) 126 | { 127 | vnet->xmit = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); 128 | 129 | if (vnet->xmit < 0) { 130 | log_error("vrid %d :: socket xmit - %m", vnet->vrid); 131 | return -1; 132 | } 133 | 134 | return 0; 135 | } 136 | 137 | 138 | /** 139 | * vrrp_net_vif_getaddr() - get IPvX addr from a VRRP interface 140 | */ 141 | int vrrp_net_vif_getaddr(struct vrrp_net *vnet) 142 | { 143 | 144 | struct ifaddrs *ifaddr, *ifa; 145 | int family; 146 | 147 | if (getifaddrs(&ifaddr) == -1) { 148 | log_error("vrid %d :: getifaddrs - %m", vnet->vrid); 149 | return -1; 150 | } 151 | 152 | /* search address */ 153 | for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { 154 | if (strcmp(ifa->ifa_name, vnet->vif.ifname) != 0) 155 | continue; 156 | 157 | if (ifa->ifa_addr == NULL) 158 | continue; 159 | 160 | family = ifa->ifa_addr->sa_family; 161 | 162 | if (family != vnet->family) 163 | continue; 164 | 165 | if (vnet->family == AF_INET) { 166 | vnet->vif.ip_addr.s_addr = 167 | ((struct sockaddr_in *) ifa->ifa_addr)-> 168 | sin_addr.s_addr; 169 | } 170 | #ifdef HAVE_IP6 171 | else { /* AF_INET6 */ 172 | struct sockaddr_in6 *src = 173 | (struct sockaddr_in6 *) ifa->ifa_addr; 174 | memcpy(&vnet->vif.ip_addr6, &src->sin6_addr, 175 | sizeof(struct in6_addr)); 176 | } 177 | #endif /* HAVE_IP6 */ 178 | } 179 | 180 | freeifaddrs(ifaddr); 181 | 182 | vrrp_net_vif_mtu(vnet); 183 | 184 | return 0; 185 | } 186 | 187 | /** 188 | * vrrp_net_vif_mtu() - get MTU of vrrp interface 189 | */ 190 | int vrrp_net_vif_mtu(struct vrrp_net *vnet) 191 | { 192 | struct ifreq ifr; 193 | 194 | strncpy(ifr.ifr_name, vnet->vif.ifname, IFNAMSIZ - 1); 195 | 196 | int fd = socket(AF_INET, SOCK_DGRAM, 0); 197 | 198 | ifr.ifr_addr.sa_family = AF_INET; 199 | 200 | if (ioctl(fd, SIOCGIFMTU, &ifr) < 0) { 201 | log_error("vrid %d :: ioctl - %m", vnet->vrid); 202 | return -1; 203 | } 204 | 205 | vnet->vif.mtu = ifr.ifr_mtu; 206 | log_debug("%s mtu : %d", vnet->vif.ifname, vnet->vif.mtu); 207 | 208 | close(fd); 209 | 210 | return 0; 211 | } 212 | 213 | 214 | /** 215 | * vrrp_net_vip_set() - register VRRP virtual IPvX addresses 216 | */ 217 | int vrrp_net_vip_set(struct vrrp_net *vnet, const char *ip) 218 | { 219 | struct vrrp_ip *vip = malloc(sizeof(struct vrrp_ip)); 220 | 221 | if (vip == NULL) { 222 | log_error("vrid %d :: malloc - %m", vnet->vrid); 223 | return -1; 224 | } 225 | 226 | /* split ip / netmask */ 227 | int status = -1; 228 | 229 | if (vnet->family == AF_INET) 230 | status = 231 | split_ip_netmask(vnet->family, ip, &vip->ip_addr, 232 | &vip->netmask); 233 | 234 | #ifdef HAVE_IP6 235 | if (vnet->family == AF_INET6) 236 | status = 237 | split_ip_netmask(vnet->family, ip, &vip->ip_addr6, 238 | &vip->netmask); 239 | #endif /* HAVE_IP6 */ 240 | 241 | if (status != 0) { 242 | fprintf(stderr, "vrid %d :: invalid IP addr %s", vnet->vrid, 243 | ip); 244 | free(vip); 245 | return -1; 246 | } 247 | 248 | list_add_tail(&vip->iplist, &vnet->vip_list); 249 | 250 | return 0; 251 | } 252 | 253 | 254 | /** 255 | * vrrp_net_invalidate_buffer() 256 | * invalidate internal buffer used to stock received pkt 257 | * vnet->__pkt 258 | */ 259 | static inline void vrrp_net_invalidate_buffer(struct vrrp_net *vnet) 260 | { 261 | vnet->__pkt.adv.version_type = 0; 262 | } 263 | 264 | /** 265 | * vrrp_net_recv() - read and check a received VRRP pkt advertisement 266 | * 267 | * @return vrrp_pkt_t 268 | */ 269 | vrrp_event_t vrrp_net_recv(struct vrrp_net *vnet, const struct vrrp *vrrp) 270 | { 271 | /* fetch pkt data received to buf */ 272 | unsigned char buf[IP_MAXPACKET]; 273 | 274 | /* read IPvX header values and fill vrrp_recv buffer __pkt */ 275 | int payload_pos = 0; 276 | ssize_t len = vnet->pkt_receive(vnet->socket, &vnet->__pkt, buf, 277 | IP_MAXPACKET, &payload_pos); 278 | 279 | /* check len */ 280 | if (len == -1) { 281 | log_error("vrid %d :: invalid pkt", vnet->vrid); 282 | return INVALID; 283 | } 284 | 285 | if ((len > vnet->vif.mtu) || (len < vnet->adv_getsize(vnet))) { 286 | log_error("vrid %d :: invalid pkt len", vnet->vrid); 287 | return INVALID; 288 | } 289 | 290 | /* read and check vrrp advertisement pkt */ 291 | struct vrrphdr *vrrpkt; 292 | 293 | vrrpkt = (struct vrrphdr *) (buf + payload_pos); 294 | 295 | /* TODO : is this really necessary ?? */ 296 | vrrp_net_invalidate_buffer(vnet); 297 | 298 | /* check VRRP pkt size (including VRRP IP address(es) and Auth data) */ 299 | unsigned int payload_size = 300 | vnet->__pkt.header.totlen - vnet->__pkt.header.len; 301 | 302 | if ((payload_size < VRRP_PKT_MINSIZE) 303 | || (payload_size > VRRP_PKT_MAXSIZE)) { 304 | log_info 305 | ("vrid %d :: Invalid pkt - Invalid packet size %d, expecting size between %ld and %ld", 306 | vrrp->vrid, payload_size, VRRP_PKT_MINSIZE, 307 | VRRP_PKT_MAXSIZE); 308 | return INVALID; 309 | } 310 | 311 | /* verify ip proto */ 312 | if (vnet->__pkt.header.proto != IPPROTO_VRRP) { 313 | log_info("vrid %d :: Invalid pkt - ip proto not valid %d", 314 | vrrp->vrid, vnet->__pkt.header.proto); 315 | return INVALID; 316 | } 317 | 318 | /* verify VRRP version */ 319 | if ((vrrpkt->version_type >> 4) != vrrp->version) { 320 | log_info 321 | ("vrid %d :: Invalid pkt - version %d mismatch, expecting %d", 322 | vrrp->vrid, vrrpkt->version_type >> 4, vrrp->version); 323 | return INVALID; 324 | } 325 | 326 | /* TTL must be 255 */ 327 | if (vnet->__pkt.header.ttl != VRRP_TTL) { 328 | log_info("vrid %d :: Invalid pkt - TTL isn't %d", vrrp->vrid, 329 | VRRP_TTL); 330 | return INVALID; 331 | } 332 | 333 | /* check if VRID is the same as the current instance */ 334 | if (vrrpkt->vrid != vrrp->vrid) { 335 | log_debug("vrid %d :: Invalid pkt - Invalid VRID %d", 336 | vrrp->vrid, vrrpkt->vrid); 337 | return VRID_MISMATCH; 338 | } 339 | 340 | /* verify VRRP checksum */ 341 | int chksum = vrrpkt->chksum; /* save checksum */ 342 | if (vnet->adv_checksum(vnet, vrrpkt, &vnet->__pkt.s_ipx, 343 | &vnet->__pkt.d_ipx) != chksum) { 344 | log_info("vrid %d :: Invalid pkt - Invalid checksum %x", 345 | vrrp->vrid, chksum); 346 | 347 | return INVALID; 348 | } 349 | /* restore checksum */ 350 | vrrpkt->chksum = chksum; 351 | 352 | /* local router is the IP address owner 353 | * (Priority equals 255) 354 | */ 355 | if (vrrp->priority == PRIO_OWNER) { 356 | log_info 357 | ("vrid %d :: Invalid pkt - *We* are the owner of IP address(es) (priority %d)", 358 | vrrp->vrid, vrrp->priority); 359 | return INVALID; 360 | } 361 | 362 | 363 | /* Auth type (RFC2338/3768) */ 364 | if (vrrp->version != RFC5798) { 365 | 366 | /* auth type must be the same locally configured */ 367 | if (vrrpkt->auth_type != vrrp->auth_type) { 368 | log_info 369 | ("vrid %d :: Invalid pkt - Invalid authentication type", 370 | vrrp->vrid); 371 | return INVALID; 372 | } 373 | 374 | /* auth type is simple */ 375 | if (vrrpkt->auth_type == SIMPLE) { 376 | uint32_t *auth_data = 377 | (uint32_t *) ((unsigned char *) vrrpkt + 378 | VRRP_PKTHDR_SIZE + 379 | vrrp->naddr * sizeof(uint32_t)); 380 | if (memcmp 381 | (auth_data, vrrp->auth_data, 382 | strlen(vrrp->auth_data)) 383 | != 0) { 384 | log_info 385 | ("vrid %d :: Invalid pkt - Invalid authentication password", 386 | vrrp->vrid); 387 | return INVALID; 388 | } 389 | } 390 | } 391 | 392 | /* count of IP address(es) and list may be the same 393 | * or generated by the owner 394 | */ 395 | if (((vrrpkt->naddr != vrrp->naddr) 396 | || (vnet->vip_compare(vnet, vrrpkt) != 0)) 397 | && (vrrpkt->priority != PRIO_OWNER) && (vrrp->version == 2)) { 398 | log_info 399 | ("vrid %d :: Invalid pkt not generated by the owner, drop it", 400 | vrrp->vrid); 401 | return INVALID; 402 | } 403 | 404 | /* advert interval must be the same as the locally configured */ 405 | if (vrrpkt->adv_int != vrrp->adv_int) { 406 | log_info 407 | ("vrid %d :: Invalid pkt - Advertisement interval mismatch\n", 408 | vrrp->vrid); 409 | return INVALID; 410 | } 411 | 412 | /* pkt is valid, keep it in internal buffer */ 413 | memcpy(&vnet->__pkt.adv, vrrpkt, sizeof(struct vrrphdr)); 414 | 415 | return PKT; 416 | } 417 | 418 | /** 419 | * vrrp_net_send - send pkt 420 | */ 421 | int vrrp_net_send(const struct vrrp_net *vnet, struct iovec *iov, size_t len) 422 | { 423 | if (iov == NULL) { 424 | log_error("vrid %d :: No data to send !?", vnet->vrid); 425 | return -1; 426 | } 427 | 428 | struct sockaddr_ll device = { 0 }; 429 | 430 | device.sll_family = AF_PACKET; 431 | device.sll_ifindex = if_nametoindex(vnet->vif.ifname); 432 | 433 | if (device.sll_ifindex == 0) { 434 | log_error("vrid %d :: if_nametoindex - %m", vnet->vrid); 435 | return -1; 436 | } 437 | 438 | struct msghdr msg = { 0 }; 439 | 440 | msg.msg_name = &device; 441 | msg.msg_namelen = sizeof(device); 442 | msg.msg_iov = iov; 443 | msg.msg_iovlen = len; 444 | msg.msg_control = NULL; 445 | msg.msg_controllen = 0; 446 | msg.msg_flags = 0; 447 | 448 | if (sendmsg(vnet->xmit, &msg, 0) < 0) { 449 | log_error("vrid %d :: sendmsg - %m", vnet->vrid); 450 | return -1; 451 | } 452 | 453 | return 0; 454 | } 455 | -------------------------------------------------------------------------------- /vrrp_net.h: -------------------------------------------------------------------------------- 1 | /* 2 | * vrrp_net.h 3 | * 4 | * Copyright (C) 2014 Arnaud Andre 5 | * 6 | * This file is part of uvrrpd. 7 | * 8 | * uvrrpd is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * uvrrpd is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with uvrrpd. If not, see . 20 | */ 21 | 22 | 23 | #ifndef _VRRP_NET_ 24 | #define _VRRP_NET_ 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | #include "vrrp_ipx.h" 37 | #include "vrrp_rfc.h" 38 | #include "list.h" 39 | 40 | /* from vrrp.h */ 41 | struct vrrp; 42 | typedef enum _vrrp_event_type vrrp_event_t; 43 | 44 | /** 45 | * constants 46 | */ 47 | #define IPPROTO_VRRP 112 48 | #define VIP_MAX 255 49 | 50 | #define VRRP_AUTH_SIZE 2*sizeof(uint32_t) 51 | #define VRRP_VIPMAX_SIZE VIP_MAX * sizeof(uint32_t) 52 | #define VRRP_PKTHDR_SIZE sizeof(struct vrrphdr) 53 | #define VRRP_PKT_MINSIZE VRRP_PKTHDR_SIZE + sizeof(uint32_t) 54 | #define VRRP_PKT_MAXSIZE VRRP_PKTHDR_SIZE + VRRP_VIPMAX_SIZE + VRRP_AUTH_SIZE 55 | 56 | #define IPHDR_SIZE sizeof(struct iphdr) 57 | 58 | /** 59 | * struct vrrp_ip - VRRP IPs addresses 60 | */ 61 | struct vrrp_ip { 62 | union vrrp_ipx_addr ipx; 63 | uint8_t netmask; 64 | struct list_head iplist; 65 | /* internal buffer for topology update pkt */ 66 | struct iovec __topology[3]; 67 | }; 68 | 69 | /** 70 | * struct vrrp_if - VRRP interface 71 | */ 72 | struct vrrp_if { 73 | char *ifname; 74 | int mtu; 75 | union vrrp_ipx_addr ipx; 76 | }; 77 | 78 | 79 | /** 80 | * struct vrrp_recv - VRRP buffer recv 81 | */ 82 | struct vrrp_recv { 83 | union vrrp_ipx_addr s_ipx; 84 | union vrrp_ipx_addr d_ipx; 85 | struct vrrp_ipx_header header; 86 | struct vrrphdr adv; 87 | }; 88 | 89 | #define ip_addr ipx.addr 90 | #define ip_saddr s_ipx.addr 91 | #define ip_daddr d_ipx.addr 92 | 93 | #ifdef HAVE_IP6 94 | #define ip_addr6 ipx.addr6 95 | #define ip_saddr6 s_ipx.addr6 96 | #define ip_daddr6 d_ipx.addr6 97 | #endif /* HAVE_IP6 */ 98 | 99 | /** 100 | * struct vrrp_net - VRRP net structure 101 | */ 102 | struct vrrp_net { 103 | /* VRRP id */ 104 | uint8_t vrid; 105 | 106 | /* VRRP interface */ 107 | struct vrrp_if vif; 108 | 109 | /* list of VRRP IP adresses */ 110 | struct list_head vip_list; 111 | 112 | /* family */ 113 | int family; 114 | 115 | /* count IP addresses */ 116 | uint8_t naddr; 117 | 118 | /* listen VRRP socket */ 119 | int socket; 120 | 121 | /* xmit VRRP socket */ 122 | int xmit; 123 | 124 | /* buffer for received pkt */ 125 | struct vrrp_recv __pkt; 126 | 127 | /* buffer for advertisement pkt */ 128 | struct iovec __adv[3]; 129 | 130 | /* family helper functions */ 131 | struct vrrp_ipx *ipx_helper; 132 | }; 133 | #define set_sockopt ipx_helper->setsockopt 134 | #define join_mgroup ipx_helper->mgroup 135 | #define vip_compare ipx_helper->viplist_cmp 136 | #define ipx_cmp ipx_helper->cmp 137 | #define pkt_receive ipx_helper->recv 138 | #define adv_checksum ipx_helper->chksum 139 | #define adv_getsize ipx_helper->getsize 140 | #define ipx_to_str ipx_helper->ipx_ntop 141 | #define str_to_ipx ipx_helper->ipx_pton 142 | 143 | 144 | /* 145 | * funcs 146 | */ 147 | void vrrp_net_init(struct vrrp_net *vnet); 148 | void vrrp_net_cleanup(struct vrrp_net *vnet); 149 | int vrrp_net_socket(struct vrrp_net *vnet); 150 | int vrrp_net_socket_xmit(struct vrrp_net *vnet); 151 | int vrrp_net_vif_getaddr(struct vrrp_net *vnet); 152 | int vrrp_net_vif_mtu(struct vrrp_net *vnet); 153 | int vrrp_net_vip_set(struct vrrp_net *vnet, const char *ip); 154 | vrrp_event_t vrrp_net_recv(struct vrrp_net *vnet, const struct vrrp *vrrp); 155 | int vrrp_net_send(const struct vrrp_net *vnet, struct iovec *iov, size_t len); 156 | 157 | #endif /* _VRRP_NET_ */ 158 | -------------------------------------------------------------------------------- /vrrp_options.c: -------------------------------------------------------------------------------- 1 | /* 2 | * vrrp_options.c 3 | * 4 | * Copyright (C) 2014 Arnaud Andre 5 | * 6 | * This file is part of uvrrpd. 7 | * 8 | * uvrrpd is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * uvrrpd is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with uvrrpd. If not, see . 20 | */ 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include "vrrp.h" 28 | #include "vrrp_net.h" 29 | #include "vrrp_options.h" 30 | #include "common.h" 31 | #include "log.h" 32 | 33 | /* from uvrrpd.c */ 34 | extern int background; 35 | extern char *loglevel; 36 | extern char *pidfile_name; 37 | extern char *ctrlfile_name; 38 | 39 | /** 40 | * vrrp_usage() 41 | */ 42 | static void vrrp_usage(void) 43 | { 44 | fprintf(stdout, 45 | "Usage: uvrrpd -v vrid -i ifname [OPTIONS] VIP1 [… VIPn]\n\n" 46 | "Mandatory options:\n" 47 | " -v, --vrid vrid Virtual router identifier\n" 48 | " -i, --interface iface Interface\n" 49 | " VIP Virtual IP(s), 1 to 255 VIPs\n\n" 50 | "Optional arguments:\n" 51 | " -p, --priority prio Priority of VRRP Instance, (0-255, default 100)\n" 52 | " -t, --time delay Time interval between advertisements\n" 53 | " Seconds in VRRPv2 (default 1s),\n" 54 | " Centiseconds in VRRPv3 (default 100cs)\n" 55 | " -T, --start-delay delay Use custom delay in INIT state, override masterdown\n" 56 | " timer\n" 57 | " Seconds in VRRPv2 (default 0s),\n" 58 | " Centiseconds in VRRPv3 (default 0cs)\n" 59 | " -P, --preempt on|off Switch preempt (default on)\n" 60 | " -r, --rfc version Specify protocol 'version'\n" 61 | " 2 (VRRPv2, RFC3768) by default,\n" 62 | " 3 (VRRPv3, RFC5798)\n" 63 | #ifdef HAVE_IP6 64 | " -6, --ipv6 IPv6 support, (only in VRRPv3)\n" 65 | #endif /* HAVE_IP6 */ 66 | " -a, --auth pass Simple text password (only in VRRPv2)\n" 67 | " -f, --foreground Execute uvrrpd in foreground\n" 68 | " -s, --script Path of hook script (default "stringify(PATH)"/vrrp_switch.sh)\n" 69 | " -F --pidfile name Use alternate pid file 'name'\n" 70 | " Default "stringify(PATHRUN)"/uvrrp_${vrid}.pid\n" 71 | " -C --control name Use alternate control file 'name'\n" 72 | " Default "stringify(PATHRUN)"/uvrrpd_ctrl.${vrid}\n" 73 | " -d, --debug\n" " -h, --help\n"); 74 | } 75 | 76 | /** 77 | * vrrp_options() - Parse command line options 78 | */ 79 | int vrrp_options(struct vrrp *vrrp, struct vrrp_net *vnet, int argc, 80 | char *argv[]) 81 | { 82 | int optc, err; 83 | unsigned long opt; /* strtoul */ 84 | 85 | static struct option const opts[] = { 86 | {"vrid", required_argument, 0, 'v'}, 87 | {"interface", required_argument, 0, 'i'}, 88 | {"priority", required_argument, 0, 'p'}, 89 | {"time", required_argument, 0, 't'}, 90 | {"start-delay", required_argument, 0, 'T'}, 91 | {"preempt", required_argument, 0, 'P'}, 92 | {"rfc", required_argument, 0, 'r'}, 93 | #ifdef HAVE_IP6 94 | {"ipv6", no_argument, 0, '6'}, 95 | #endif /* HAVE_IP6 */ 96 | {"auth", required_argument, 0, 'a'}, 97 | {"foreground", no_argument, 0, 'f'}, 98 | {"script", required_argument, 0, 's'}, 99 | {"pidfile", required_argument, 0, 'F'}, 100 | {"control", required_argument, 0, 'C'}, 101 | {"debug", no_argument, 0, 'd'}, 102 | {"help", no_argument, 0, 'h'}, 103 | {NULL, 0, 0, 0} 104 | }; 105 | 106 | while ((optc = 107 | getopt_long(argc, argv, 108 | #ifdef HAVE_IP6 109 | "v:i:p:t:T:P:r:6a:fs:F:C:dh", 110 | #else 111 | "v:i:p:t:T:P:r:a:fs:F:C:dh", 112 | #endif /* HAVE_IP6 */ 113 | opts, 114 | 115 | NULL)) != EOF) { 116 | switch (optc) { 117 | 118 | /* vrid */ 119 | case 'v': 120 | err = mystrtoul(&opt, optarg, VRID_MAX); 121 | if (err == -ERANGE || (err == 0 && opt == 0)) { 122 | fprintf(stderr, "1 < vrid < 255\n"); 123 | vrrp_usage(); 124 | return err; 125 | } 126 | if (err == -EINVAL) { 127 | fprintf(stderr, 128 | "Error parsing \"%s\" as a number\n", 129 | optarg); 130 | vrrp_usage(); 131 | return err; 132 | } 133 | 134 | vrrp->vrid = vnet->vrid = (uint8_t) opt; 135 | break; 136 | 137 | /* interface */ 138 | case 'i': 139 | vnet->vif.ifname = strndup(optarg, IFNAMSIZ); 140 | if (vnet->vif.ifname == NULL) { 141 | perror("strndup"); 142 | return -1; 143 | } 144 | break; 145 | 146 | /* priority */ 147 | case 'p': 148 | err = mystrtoul(&opt, optarg, VRRP_PRIO_MAX); 149 | if (err == -ERANGE) { 150 | fprintf(stderr, "0 < priority < 255\n"); 151 | vrrp_usage(); 152 | return -1; 153 | } 154 | if (err == -EINVAL) { 155 | fprintf(stderr, 156 | "Error parsing \"%s\" as a number\n", 157 | optarg); 158 | vrrp_usage(); 159 | return err; 160 | } 161 | 162 | vrrp->priority = (uint8_t) opt; 163 | break; 164 | 165 | /* delay */ 166 | case 't': 167 | err = mystrtoul(&opt, optarg, ADVINT_MAX); 168 | if (err == -ERANGE) { 169 | vrrp_usage(); 170 | return -1; 171 | } 172 | if (err == -EINVAL) { 173 | fprintf(stderr, 174 | "Error parsing \"%s\" as a number\n", 175 | optarg); 176 | vrrp_usage(); 177 | return err; 178 | } 179 | 180 | vrrp->adv_int = (uint16_t) opt; 181 | break; 182 | 183 | /* start delay */ 184 | case 'T': 185 | err = mystrtoul(&opt, optarg, ADVINT_MAX); 186 | if (err == -ERANGE) { 187 | vrrp_usage(); 188 | return -1; 189 | } 190 | if (err == -EINVAL) { 191 | fprintf(stderr, 192 | "Error parsing \"%s\" as a number\n", 193 | optarg); 194 | vrrp_usage(); 195 | return err; 196 | } 197 | 198 | vrrp->start_delay = (uint16_t) opt; 199 | break; 200 | 201 | /* preempt mode */ 202 | case 'P': 203 | if (matches(optarg, "on")) 204 | vrrp->preempt = TRUE; 205 | else if (matches(optarg, "off")) 206 | vrrp->preempt = FALSE; 207 | else { 208 | fprintf(stderr, 209 | "preempt mode 'on' or 'off', by default 'on'\n"); 210 | vrrp_usage(); 211 | return -1; 212 | } 213 | break; 214 | 215 | /* RFC - version */ 216 | case 'r': 217 | err = mystrtoul(&opt, optarg, RFC5798); 218 | if (err == -ERANGE || (err == 0 && opt < RFC3768)) { 219 | fprintf(stderr, "Version 2 or 3 : %s\n", optarg); 220 | vrrp_usage(); 221 | return -1; 222 | } 223 | if (err == -EINVAL) { 224 | fprintf(stderr, 225 | "Error parsing \"%s\" as a number\n", 226 | optarg); 227 | vrrp_usage(); 228 | return err; 229 | } 230 | 231 | vrrp->version = (uint8_t) opt; 232 | break; 233 | 234 | #ifdef HAVE_IP6 235 | /* IPv6 */ 236 | case '6': /* Force RFC5798/VRRPv3 */ 237 | vrrp->version = RFC5798; 238 | vnet->family = AF_INET6; 239 | break; 240 | #endif /* HAVE_IP6 */ 241 | 242 | /* auth */ 243 | case 'a': /* only SIMPLE password supported */ 244 | if (strlen(optarg) > VRRP_AUTH_PASS_LEN) { 245 | fprintf(stderr, 246 | "Password too long (8 char max)\n"); 247 | vrrp_usage(); 248 | return -1; 249 | } 250 | vrrp->auth_data = strndup(optarg, VRRP_AUTH_PASS_LEN); 251 | vrrp->auth_type = SIMPLE; 252 | /* hide passwd from ps */ 253 | strncpy(optarg, "********", strlen(optarg)); 254 | break; 255 | 256 | /* foreground */ 257 | case 'f': 258 | background = 0; 259 | break; 260 | 261 | 262 | /* script */ 263 | case 's': 264 | vrrp->scriptname = strndup(optarg, VRRP_SCRIPT_MAX); 265 | if (vrrp->scriptname == NULL) { 266 | perror("strndup"); 267 | return -1; 268 | } 269 | break; 270 | 271 | /* pidfile */ 272 | case 'F': 273 | pidfile_name = strndup(optarg, NAME_MAX + PATH_MAX); 274 | break; 275 | 276 | /* control file (fifo) */ 277 | case 'C': 278 | ctrlfile_name = strndup(optarg, NAME_MAX + PATH_MAX); 279 | break; 280 | 281 | /* debug */ 282 | case 'd': 283 | loglevel = strndup("debug", 6); 284 | break; 285 | 286 | /* help */ 287 | case 'h': 288 | default: 289 | vrrp_usage(); 290 | return -1; 291 | break; 292 | } 293 | } 294 | 295 | /* Fetch virtual IP addresses */ 296 | if (optind == argc) { 297 | fprintf(stderr, "Specify at least one virtual IP addr !\n"); 298 | vrrp_usage(); 299 | return -1; 300 | } 301 | 302 | /* Number of IP addresses */ 303 | vrrp->naddr = vnet->naddr = argc - optind; 304 | 305 | /* Register vrrp_vip addresses */ 306 | while (optind != argc) { 307 | if (vrrp_net_vip_set(vnet, argv[optind]) != 0) { 308 | fprintf(stderr, "Invalid IP\n"); 309 | vrrp_usage(); 310 | return -1; 311 | } 312 | ++optind; 313 | } 314 | 315 | /* minimal configuration */ 316 | if (vrrp->vrid == 0) { 317 | fprintf(stderr, "Specify VRRP id\n"); 318 | vrrp_usage(); 319 | return -1; 320 | } 321 | if (vnet->vif.ifname == NULL) { 322 | fprintf(stderr, "Specify VRRP interface\n"); 323 | vrrp_usage(); 324 | return -1; 325 | } 326 | 327 | /* default adv int */ 328 | if ((vrrp->version == RFC3768) && (vrrp->adv_int == 0)) 329 | vrrp->adv_int = 1; 330 | else if ((vrrp->version == RFC5798) && (vrrp->adv_int == 0)) 331 | vrrp->adv_int = 100; 332 | 333 | /* Get IP addresse from interface name */ 334 | return vrrp_net_vif_getaddr(vnet); 335 | } 336 | -------------------------------------------------------------------------------- /vrrp_options.h: -------------------------------------------------------------------------------- 1 | /* 2 | * vrrp_options.h 3 | * 4 | * Copyright (C) 2014 Arnaud Andre 5 | * 6 | * This file is part of uvrrpd. 7 | * 8 | * uvrrpd is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * uvrrpd is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with uvrrpd. If not, see . 20 | */ 21 | 22 | #ifndef _VRRP_OPTIONS_H_ 23 | #define _VRRP_OPTIONS_H_ 24 | 25 | int vrrp_options(struct vrrp *vrrp, struct vrrp_net *vnet, int argc, 26 | char *argv[]); 27 | 28 | #endif /* _VRRP_OPTIONS_H_ */ 29 | -------------------------------------------------------------------------------- /vrrp_rfc.h: -------------------------------------------------------------------------------- 1 | /* 2 | * vrrp_rfc.h 3 | * 4 | * Copyright (C) 2014 Arnaud Andre 5 | * 6 | * This file is part of uvrrpd. 7 | * 8 | * uvrrpd is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * uvrrpd is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with uvrrpd. If not, see . 20 | */ 21 | 22 | #ifndef _VRRP_RFC_H_ 23 | #define _VRRP_RFC_H_ 24 | 25 | #include 26 | 27 | /* 28 | * vrrphdr 29 | * Header structure for rfc3768 et rfc5798 30 | */ 31 | 32 | struct vrrphdr { 33 | uint8_t version_type; /* 0-3=version, 4-7=type */ 34 | uint8_t vrid; 35 | uint8_t priority; 36 | uint8_t naddr; 37 | union { 38 | 39 | /* rfc 3768 */ 40 | struct { 41 | uint8_t auth_type; 42 | uint8_t adv_int; 43 | }; 44 | 45 | /* rfc 5798 */ 46 | uint16_t max_adv_int; /* 0-3=rsvd, 4-5=adv_int */ 47 | }; 48 | uint16_t chksum; 49 | 50 | /* virtual IPs start here */ 51 | 52 | } __attribute__ ((packed)); 53 | 54 | #endif /* _VRRP_RFC_H_ */ 55 | -------------------------------------------------------------------------------- /vrrp_state.c: -------------------------------------------------------------------------------- 1 | /* 2 | * vrrp_state.c 3 | * 4 | * Copyright (C) 2014 Arnaud Andre 5 | * 6 | * This file is part of uvrrpd. 7 | * 8 | * uvrrpd is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * uvrrpd is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with uvrrpd. If not, see . 20 | */ 21 | 22 | #include 23 | 24 | #include "vrrp.h" 25 | #include "vrrp_net.h" 26 | #include "vrrp_adv.h" 27 | #include "vrrp_arp.h" 28 | #include "vrrp_na.h" 29 | #include "vrrp_exec.h" 30 | 31 | #include "log.h" 32 | #include "bits.h" 33 | #include "uvrrpd.h" 34 | 35 | extern unsigned long reg; 36 | 37 | /** 38 | * switching state functions 39 | */ 40 | static int vrrp_state_goto_master(struct vrrp *vrrp, struct vrrp_net *vnet); 41 | static int vrrp_state_goto_backup(struct vrrp *vrrp, struct vrrp_net *vnet); 42 | 43 | /** 44 | * vrrp_state_init() - Initial state of VRRP instance 45 | * 46 | * Switch to master or backup state 47 | */ 48 | int vrrp_state_init(struct vrrp *vrrp, struct vrrp_net *vnet) 49 | { 50 | log_notice("vrid %d :: %s", vrrp->vrid, "init"); 51 | 52 | /* Hook init */ 53 | vrrp_exec(vrrp, vnet, INIT); 54 | 55 | /* init Master_Adver_Interval */ 56 | vrrp->master_adv_int = vrrp->adv_int; 57 | log_debug("%d", vrrp->master_adv_int); 58 | 59 | /* router owns ip address(es) */ 60 | if (vrrp->priority == 255) { 61 | log_debug("%s %d :%s", "priority", vrrp->priority, 62 | "router owns VIP address(es)"); 63 | return vrrp_state_goto_master(vrrp, vnet); 64 | } 65 | 66 | return vrrp_state_goto_backup(vrrp, vnet); 67 | } 68 | 69 | /** 70 | * vrrp_state_backup() - handle backup state 71 | */ 72 | int vrrp_state_backup(struct vrrp *vrrp, struct vrrp_net *vnet) 73 | { 74 | int event = vrrp_listen(vrrp, vnet); 75 | char straddr[INET6_ADDRSTRLEN]; 76 | 77 | switch (event) { 78 | case TIMER: /* TIMER expired */ 79 | log_notice("vrid %d :: %s", vrrp->vrid, 80 | "masterdown_timer expired"); 81 | 82 | vrrp_state_goto_master(vrrp, vnet); 83 | break; 84 | 85 | case PKT: /* PKT received */ 86 | /* Must be impossible, invalid received adv buffer */ 87 | if (vrrp_adv_get_version(vnet) == 0) { 88 | log_error("vrid %d :: %s", vrrp->vrid, 89 | "recv buffer empty !?"); 90 | break; 91 | } 92 | 93 | log_debug("vrid %d :: %s", vrrp->vrid, 94 | "pkt received from current master"); 95 | log_debug("vrid %d :: %s:%d %s:%d %s:%s", vrrp->vrid, "prio", 96 | vrrp->priority, "prio recv", 97 | vrrp_adv_get_priority(vnet), "preempt", 98 | STR_PREEMPT(vrrp->preempt)); 99 | 100 | /* Priority of received pkt is 0 101 | * => set Master_Down_Timer to skew_time 102 | */ 103 | if (vrrp_adv_get_priority(vnet) == 0) { 104 | log_info("vrid %d :: %s", vrrp->vrid, 105 | "receive packet with priority 0"); 106 | log_notice("vrid %d :: %s %d", vrrp->vrid, 107 | "set masterdown_timer to skew_time", 108 | SKEW_TIME(vrrp)); 109 | 110 | VRRP_SET_SKEW_TIME(vrrp); 111 | break; 112 | } 113 | 114 | /* Master has send its adv pkt, or preemption mode is false 115 | */ 116 | if ((vrrp->preempt == FALSE) || 117 | (vrrp_adv_get_priority(vnet) >= vrrp->priority)) { 118 | 119 | /* RFC5798: Set Master_Adver_Interval to 120 | * Advertisement_Interval 121 | */ 122 | if (vrrp->version == RFC5798) 123 | vrrp->master_adv_int = 124 | vrrp_adv_get_advint(vnet); 125 | 126 | VRRP_SET_MASTERDOWN_TIMER(vrrp); 127 | break; 128 | } 129 | 130 | /* Our priority is greater and preemption mode is true 131 | * So we discard advertisement. 132 | * Maybe we'll become Master if Master_Down_Timer expire, 133 | */ 134 | log_info("vrid %d :: %s", vrrp->vrid, "discard advertisement"); 135 | 136 | break; 137 | 138 | case SIGNAL: 139 | log_debug("vrid %d :: signal", vrrp->vrid); 140 | case CTRL_FIFO: 141 | /* shutdown/reload event ? */ 142 | if (test_and_clear_bit(UVRRPD_RELOAD, ®)) { 143 | vrrp_timer_clear(&vrrp->masterdown_timer); 144 | vrrp->state = INIT; 145 | } 146 | 147 | break; 148 | 149 | case VRID_MISMATCH: 150 | /* ignore VRRP pkt with different vrid */ 151 | break; 152 | 153 | case INVALID: 154 | log_warning("vrid %d :: %s %s, %s", vrrp->vrid, 155 | "receive an invalid advertisement packet from", 156 | vrrp_adv_addr_to_str(vnet, straddr), "ignore it"); 157 | 158 | break; 159 | 160 | default: 161 | log_error("vrid %d :: %s r:%d", vrrp->vrid, "unknown event", 162 | event); 163 | break; 164 | } 165 | 166 | return event; 167 | } 168 | 169 | /** 170 | * vrrp_state_master() - handle master state 171 | */ 172 | int vrrp_state_master(struct vrrp *vrrp, struct vrrp_net *vnet) 173 | { 174 | int event = vrrp_listen(vrrp, vnet); 175 | 176 | switch (event) { 177 | case TIMER: /* TIMER expired */ 178 | /* adv_timer expired, time to send another */ 179 | log_info("vrid %d :: %s", vrrp->vrid, "adv_timer expired"); 180 | vrrp_adv_send(vnet); 181 | VRRP_SET_ADV_TIMER(vrrp); 182 | break; 183 | 184 | case PKT: /* PKT received */ 185 | /* Must be impossible, invalid adv received buffer */ 186 | if (vrrp_adv_get_version(vnet) == 0) { 187 | log_error("vrid %d :: %s", vrrp->vrid, 188 | "recv buffer empty !?"); 189 | break; 190 | } 191 | 192 | log_debug("vrid %d :: %s:%d %s:%d %s:%s", vrrp->vrid, "prio", 193 | vrrp->priority, "prio recv", 194 | vrrp_adv_get_priority(vnet), "preempt", 195 | STR_PREEMPT(vrrp->preempt)); 196 | 197 | /* Priority of received pkt is 0 198 | * We send an advertisement 199 | * and rearm Adv_timer 200 | */ 201 | if (vrrp_adv_get_priority(vnet) == 0) { 202 | log_info("vrid %d :: %s", vrrp->vrid, 203 | "receive packet with priority 0"); 204 | vrrp_adv_send(vnet); 205 | VRRP_SET_ADV_TIMER(vrrp); 206 | break; 207 | } 208 | 209 | /* Priority of received pkt is greater than our, 210 | * switch to backup 211 | */ 212 | if (vrrp_adv_get_priority(vnet) > vrrp->priority) { 213 | 214 | log_notice("vrid %d :: %s", vrrp->vrid, 215 | "receive packet with higher priority"); 216 | 217 | vrrp_state_goto_backup(vrrp, vnet); 218 | break; 219 | } 220 | 221 | /* Priority of received pkt is equal, but 222 | * primary IP address of the sender is greater than 223 | * the local primary IP address, 224 | * switch to backup 225 | */ 226 | if (vrrp_adv_get_priority(vnet) == vrrp->priority) { 227 | if (vrrp_adv_addr_cmp(vnet) > 0) { 228 | log_notice("vrid %d :: %s", vrrp->vrid, 229 | "Same priority !"); 230 | log_notice("vrid %d :: %s", vrrp->vrid, 231 | "Primary IP address of the sender greater than the local primary address"); 232 | 233 | vrrp_state_goto_backup(vrrp, vnet); 234 | break; 235 | } 236 | 237 | log_notice("vrid %d :: %s", vrrp->vrid, 238 | "Same priority !"); 239 | log_notice("vrid %d :: %s", vrrp->vrid, 240 | "Local primary address is greater than the primary IP address of the sender"); 241 | 242 | } 243 | 244 | /* We have the greatest priority */ 245 | log_info("vrid %d :: %s", vrrp->vrid, "discard advertisement"); 246 | 247 | break; 248 | 249 | case SIGNAL: 250 | log_debug("vrid %d :: signal", vrrp->vrid); 251 | case CTRL_FIFO: 252 | 253 | /* shutdown/reload event ? */ 254 | if (test_and_clear_bit(UVRRPD_RELOAD, ®)) { 255 | vrrp_timer_clear(&vrrp->adv_timer); 256 | vrrp_adv_send_zero(vnet); 257 | /* berk */ 258 | vrrp_exec(vrrp, vnet, BACKUP); 259 | vrrp->state = INIT; 260 | } 261 | 262 | break; 263 | 264 | case VRID_MISMATCH: 265 | /* ignore VRRP pkt with different vrid */ 266 | break; 267 | 268 | case INVALID: 269 | log_warning("vrid %d :: invalid event", vrrp->vrid); 270 | break; 271 | 272 | default: 273 | log_error("vrid %d :: %s r:%d", vrrp->vrid, "unknown event", 274 | event); 275 | break; 276 | } 277 | 278 | return event; 279 | } 280 | 281 | 282 | /** 283 | * vrrp_state_goto_master() - switch state to master 284 | */ 285 | static int vrrp_state_goto_master(struct vrrp *vrrp, struct vrrp_net *vnet) 286 | { 287 | log_notice("vrid %d :: %s -> %s", vrrp->vrid, 288 | STR_STATE(vrrp->state), "master"); 289 | 290 | vrrp->state = MASTER; 291 | 292 | vrrp_adv_send(vnet); 293 | 294 | if (vnet->family == AF_INET) 295 | vrrp_arp_send(vnet); 296 | #ifdef HAVE_IP6 297 | else if (vnet->family == AF_INET6) 298 | vrrp_na_send(vnet); 299 | #endif /* HAVE_IP6 */ 300 | 301 | /* script */ 302 | vrrp_exec(vrrp, vnet, vrrp->state); 303 | 304 | /* reset masterdown_timer && set ADV timer */ 305 | vrrp_timer_clear(&vrrp->masterdown_timer); 306 | VRRP_SET_ADV_TIMER(vrrp); 307 | 308 | return 0; 309 | } 310 | 311 | 312 | /** 313 | * vrrp_state_goto_backup() - switch state to backup 314 | */ 315 | static int vrrp_state_goto_backup(struct vrrp *vrrp, struct vrrp_net *vnet) 316 | { 317 | log_notice("vrid %d :: %s -> %s", vrrp->vrid, 318 | STR_STATE(vrrp->state), "backup"); 319 | 320 | int previous_state = vrrp->state; 321 | vrrp->state = BACKUP; 322 | 323 | log_debug("%s:%s", STR_STATE(previous_state), STR_STATE(vrrp->state)); 324 | 325 | /* script */ 326 | if (previous_state != INIT) 327 | vrrp_exec(vrrp, vnet, vrrp->state); 328 | 329 | /* RFC5798, use of master_adv_int */ 330 | if (vrrp->version == RFC5798) { 331 | if (previous_state == INIT) 332 | vrrp->master_adv_int = vrrp->adv_int; 333 | else if (previous_state == MASTER) 334 | vrrp->master_adv_int = vrrp_adv_get_advint(vnet); 335 | } 336 | 337 | /* clear adv timer && set masterdown_timer */ 338 | vrrp_timer_clear(&vrrp->adv_timer); 339 | if ((previous_state == INIT) && (vrrp->start_delay != 0)) { 340 | log_notice("vrid %d :: applying start_delay %d%s", 341 | vrrp->vrid, vrrp->start_delay, 342 | (vrrp->version == 3 ? "cs":"s") 343 | ); 344 | VRRP_SET_STARTDELAY_TIMER(vrrp); 345 | } 346 | else 347 | VRRP_SET_MASTERDOWN_TIMER(vrrp); 348 | 349 | log_debug("%d %d", vrrp->master_adv_int, 350 | 3 * vrrp->master_adv_int + SKEW_TIME(vrrp)); 351 | return 0; 352 | } 353 | -------------------------------------------------------------------------------- /vrrp_state.h: -------------------------------------------------------------------------------- 1 | /* 2 | * vrrp_state.h - VRRP state machine 3 | * 4 | * Copyright (C) 2014 Arnaud Andre 5 | * 6 | * This file is part of uvrrpd. 7 | * 8 | * uvrrpd is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * uvrrpd is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with uvrrpd. If not, see . 20 | */ 21 | 22 | #ifndef _VRRP_STATE_H_ 23 | #define _VRRP_STATE_H_ 24 | 25 | /** 26 | * vrrp_state - VRRP states 27 | */ 28 | typedef enum { 29 | INIT, 30 | BACKUP, 31 | MASTER 32 | } vrrp_state; 33 | 34 | 35 | #define STR_STATE(s) (s == INIT ? "init" : \ 36 | ((s == BACKUP) ? "backup" : "master")) 37 | 38 | int vrrp_state_init(struct vrrp *vrrp, struct vrrp_net *vnet); 39 | int vrrp_state_master(struct vrrp *vrrp, struct vrrp_net *vnet); 40 | int vrrp_state_backup(struct vrrp *vrrp, struct vrrp_net *vnet); 41 | 42 | #endif /* _VRRP_STATE_H_ */ 43 | -------------------------------------------------------------------------------- /vrrp_switch.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | state=$1 4 | vrid=$2 5 | ifname=$3 6 | priority=$4 7 | adv_int=$5 8 | naddr=$6 9 | family=$7 10 | ips=$8 11 | 12 | echo "state\t\t$state 13 | vrid\t\t$vrid 14 | ifname\t\t$ifname 15 | priority\t$priority 16 | adv_int\t\t$adv_int 17 | naddr\t\t$naddr 18 | ips\t\t$ips" > /tmp/state.vrrp_${vrid}_${ifname} 19 | 20 | interface=${ifname}_${vrid} 21 | 22 | echo $ips 23 | 24 | case "$state" in 25 | 26 | "init" ) 27 | # adjust sysctl 28 | sysctl -w net.ipv4.conf.all.rp_filter=0 29 | sysctl -w net.ipv4.conf.all.arp_ignore=1 30 | sysctl -w net.ipv4.conf.all.arp_announce=2 31 | sysctl -w net.ipv4.conf.default.rp_filter=0 32 | sysctl -w net.ipv4.conf.default.arp_ignore=1 33 | sysctl -w net.ipv4.conf.default.arp_announce=2 34 | 35 | if [ -f /proc/net/if_inet6 ]; then 36 | sysctl -w net.ipv6.conf.all.autoconf=0 37 | sysctl -w net.ipv6.conf.all.accept_ra=0 38 | sysctl -w net.ipv6.conf.all.forwarding=1 39 | sysctl -w net.ipv6.conf.default.autoconf=0 40 | sysctl -w net.ipv6.conf.default.accept_ra=0 41 | sysctl -w net.ipv6.conf.default.forwarding=1 42 | fi 43 | 44 | ;; 45 | 46 | "master" ) 47 | # create macvlan interface 48 | HEXA_VRID=$(printf %x $vrid) 49 | ip link add link $ifname address 00:00:5E:00:01:$HEXA_VRID $interface type macvlan 50 | 51 | # set virtual ips addresses 52 | OIFS=$IFS 53 | IFS=',' 54 | 55 | for ip in $ips; do 56 | ip -$family addr add $ip dev $interface 57 | done 58 | ip link set dev $interface up 59 | 60 | IFS=$OIFS 61 | 62 | ;; 63 | 64 | "backup" ) 65 | # destroy macvlan interface 66 | ip link del $interface 67 | 68 | ;; 69 | esac 70 | 71 | exit 0 72 | -------------------------------------------------------------------------------- /vrrp_timer.c: -------------------------------------------------------------------------------- 1 | /* 2 | * vrrp_timer.c - functions manipulating VRRP timers 3 | * 4 | * Copyright (C) 2014 Arnaud Andre 5 | * 6 | * This file is part of uvrrpd. 7 | * 8 | * uvrrpd is free software: you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation, either version 3 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * uvrrpd is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with uvrrpd. If not, see . 20 | * 21 | * 22 | * These functions use clock_gettime() and CLOCK_MONOTONIC_RAW 23 | * which access to a raw hardware-based time that is not subject to 24 | * NTP adjustements. 25 | */ 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #include "vrrp_timer.h" 33 | #include "log.h" 34 | 35 | /* 1s for timespec operations */ 36 | #define NANOUL 1000000000 37 | #define CENTUL 10000000 38 | 39 | /** 40 | * timespec_substract() - substract two timestamp 41 | * 42 | * Subtract the `struct timespec' values X and Y, 43 | * The difference is stored in timespect result. 44 | * @return 1 if the difference is negative, otherwise 0. 45 | */ 46 | static inline int timespec_substract(struct timespec *result, 47 | struct timespec *x, struct timespec *y) 48 | { 49 | /* Perform the carry for the later subtraction by updating y. */ 50 | if (x->tv_nsec < y->tv_nsec) { 51 | time_t nsec = (y->tv_nsec - x->tv_nsec) / NANOUL + 1; 52 | y->tv_nsec -= NANOUL * nsec; 53 | y->tv_sec += nsec; 54 | } 55 | if (x->tv_nsec - y->tv_nsec > NANOUL) { 56 | time_t nsec = (x->tv_nsec - y->tv_nsec) / NANOUL; 57 | y->tv_nsec += NANOUL * nsec; 58 | y->tv_sec -= nsec; 59 | } 60 | 61 | /* Compute the time remaining to wait. 62 | tv_nsec is certainly positive. */ 63 | result->tv_sec = x->tv_sec - y->tv_sec; 64 | result->tv_nsec = x->tv_nsec - y->tv_nsec; 65 | 66 | /* Return 1 if result is negative. */ 67 | return x->tv_sec < y->tv_sec; 68 | } 69 | 70 | /** 71 | * vrrp_timer_set() - set timer and reset delta 72 | * 73 | * @delay 74 | * @return -1 if clock_gettime() fail, 0 else 75 | */ 76 | int vrrp_timer_set(struct vrrp_timer *timer, time_t delay, long delay_cs) 77 | { 78 | if (clock_gettime(CLOCK_MONOTONIC_RAW, &timer->ts) == -1) { 79 | log_error("clock_gettime: %m"); 80 | return -1; 81 | } 82 | 83 | timer->ts.tv_sec += delay; 84 | timer->ts.tv_nsec += delay_cs * CENTUL; 85 | 86 | #ifdef DEBUG 87 | log_debug("delay %ld", delay); 88 | log_debug("delay_cs %ld", delay_cs); 89 | 90 | log_debug("timer->ts.tv_sec %ld", timer->ts.tv_sec); 91 | log_debug("timer->ts.tv_nsec %ld", timer->ts.tv_nsec); 92 | #endif /* DEBUG */ 93 | 94 | /* reset delta */ 95 | timer->delta.tv_sec = 0; 96 | timer->delta.tv_nsec = 0; 97 | 98 | return 0; 99 | } 100 | 101 | /** 102 | * vrrp_timer_clear() - clear (reset) timer 103 | */ 104 | void vrrp_timer_clear(struct vrrp_timer *timer) 105 | { 106 | if (timer == NULL) 107 | return; 108 | 109 | timer->ts.tv_sec = 0; 110 | timer->ts.tv_nsec = 0; 111 | timer->delta.tv_sec = 0; 112 | timer->delta.tv_nsec = 0; 113 | } 114 | 115 | /** 116 | * vrrp_timer_is_running() - determine if a timer is 117 | * currently used 118 | * 119 | * @return 1 if running, 0 else 120 | */ 121 | int vrrp_timer_is_running(struct vrrp_timer *timer) 122 | { 123 | if ((timer->ts.tv_sec != 0) || (timer->ts.tv_nsec != 0)) 124 | return 1; 125 | 126 | return 0; 127 | } 128 | 129 | /** 130 | * vrrp_timer_update() - update a timer 131 | * 132 | * @return -1 if clock_gettime() failed 133 | * @return ETIME if timer is expired 134 | * @return 0 if timer is successfully updated 135 | */ 136 | int vrrp_timer_update(struct vrrp_timer *timer) 137 | { 138 | struct timespec ts; 139 | 140 | if (clock_gettime(CLOCK_MONOTONIC_RAW, &ts) == -1) { 141 | log_error("clock_gettime: %m"); 142 | return -1; /* TODO die() */ 143 | } 144 | 145 | if (timespec_substract(&timer->delta, &timer->ts, &ts)) { 146 | log_debug("current timer expired"); 147 | return 1; 148 | } 149 | 150 | timer->ts.tv_sec = ts.tv_sec + timer->delta.tv_sec; 151 | timer->ts.tv_nsec = ts.tv_nsec + timer->delta.tv_nsec; 152 | 153 | #ifdef DEBUG 154 | log_debug("timer->ts.tv_sec %ld", timer->ts.tv_sec); 155 | log_debug("timer->ts.tv_nsec %ld", timer->ts.tv_nsec); 156 | #endif /* DEBUG */ 157 | 158 | return 0; 159 | } 160 | 161 | /** 162 | * vrrp_timer_is_expired() - check if a timer is expired 163 | * 164 | */ 165 | int vrrp_timer_is_expired(struct vrrp_timer *timer) 166 | { 167 | if (vrrp_timer_update(timer) 168 | || ((timer->ts.tv_sec <= 0) && (timer->ts.tv_nsec <= 0))) 169 | return 1; 170 | 171 | return 0; 172 | } 173 | -------------------------------------------------------------------------------- /vrrp_timer.h: -------------------------------------------------------------------------------- 1 | /* 2 | * vrrp_timer.h - functions manipulating VRRP timers 3 | * 4 | * These functions use clock_gettime() and CLOCK_MONOTONIC_RAW 5 | * which use a raw hardware-based time that is not subject to 6 | * NTP adjustements. 7 | * 8 | * Copyright (C) 2014 Arnaud Andre 9 | * 10 | * This file is part of uvrrpd. 11 | * 12 | * uvrrpd is free software: you can redistribute it and/or modify 13 | * it under the terms of the GNU General Public License as published by 14 | * the Free Software Foundation, either version 3 of the License, or 15 | * (at your option) any later version. 16 | * 17 | * uvrrpd is distributed in the hope that it will be useful, 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | * GNU General Public License for more details. 21 | * 22 | * You should have received a copy of the GNU General Public License 23 | * along with uvrrpd. If not, see . 24 | */ 25 | 26 | 27 | #ifndef _VRRP_TIMER_H_ 28 | #define _VRRP_TIMER_H_ 29 | 30 | #include 31 | 32 | /** 33 | * struct vrrp_timer 34 | * 35 | * @ts timestamp in ns 36 | * @delta time since the last update 37 | */ 38 | struct vrrp_timer { 39 | struct timespec ts; 40 | struct timespec delta; 41 | }; 42 | 43 | /* prototype functions */ 44 | int vrrp_timer_set(struct vrrp_timer *timer, time_t delay, long delay_cs); 45 | void vrrp_timer_clear(struct vrrp_timer *timer); 46 | int vrrp_timer_is_running(struct vrrp_timer *timer); 47 | int vrrp_timer_update(struct vrrp_timer *timer); 48 | int vrrp_timer_is_expired(struct vrrp_timer *timer); 49 | 50 | /* Specific VRRP timer macros */ 51 | #define SKEW_TIME( v ) \ 52 | ( (256 - v->priority) * \ 53 | (v->version==3?v->master_adv_int:1) / 256 ) 54 | 55 | #define MASTERDOWN_INT( v ) \ 56 | (3 * v->master_adv_int + SKEW_TIME( v )) 57 | 58 | #define VRRP_SET_ADV_TIMER( v ) \ 59 | vrrp_timer_set(&v->adv_timer, \ 60 | (v->version == 3 ? 0:v->adv_int), \ 61 | (v->version == 3 ? v->master_adv_int:0)) 62 | 63 | #define VRRP_SET_MASTERDOWN_TIMER( v ) \ 64 | vrrp_timer_set(&v->masterdown_timer, \ 65 | (v->version == 3 ? 0:MASTERDOWN_INT( v )), \ 66 | (v->version == 3 ? MASTERDOWN_INT( v ):0)) 67 | 68 | #define VRRP_SET_SKEW_TIME( v ) \ 69 | vrrp_timer_set(&v->masterdown_timer, \ 70 | (v->version == 3 ? 0:SKEW_TIME( v )), \ 71 | (v->version == 3 ? SKEW_TIME( v ):0)) 72 | 73 | #define VRRP_SET_STARTDELAY_TIMER( v ) \ 74 | vrrp_timer_set(&v->masterdown_timer, \ 75 | (v->version == 3 ? 0:v->start_delay), \ 76 | (v->version == 3 ? v->start_delay:0)) 77 | 78 | #endif /* _VRRP_TIMER_H_ */ 79 | --------------------------------------------------------------------------------