├── .gitignore ├── ChangeLog ├── LICENSE ├── Makefile ├── README ├── README.md ├── nd-proxy.c ├── ndppd-init-debian-jessi ├── ndppd.1 ├── ndppd.conf-dist ├── ndppd.conf.5 ├── ndppd.service └── src ├── address.cc ├── address.h ├── conf.cc ├── conf.h ├── iface.cc ├── iface.h ├── logger.cc ├── logger.h ├── nd-netlink.cc ├── nd-netlink.h ├── ndppd.cc ├── ndppd.h ├── proxy.cc ├── proxy.h ├── ptr.h ├── route.cc ├── route.h ├── rule.cc ├── rule.h ├── session.cc └── session.h /.gitignore: -------------------------------------------------------------------------------- 1 | ndppd.conf 2 | ndppd 3 | *.o 4 | ndppd.conf.5.gz 5 | ndppd.1.gz 6 | 7 | -------------------------------------------------------------------------------- /ChangeLog: -------------------------------------------------------------------------------- 1 | 2017-01-07 Johnathan Sharratt 2 | 3 | * Version 0.2.6 4 | 5 | * Added a new configuration setting named "deadtime" which allows 6 | sessions that never made it the VALID to have a different (i.e. 7 | shorter) life before they are removed (and potentially retried) 8 | (defauilt is the same value as usual TTL for backwards compatibility) 9 | 10 | * Added a new configuration setting named "autowire" in the proxy 11 | section (default is off) 12 | 13 | * If the "autowire" setting is on, then upon receiving a NDP 14 | Neighbor Advertisment from one of the rule interfaces, a route will 15 | be automatically added into the linux IP routing tables thus allowing 16 | for a full featured gateway when IPv6 forwarding is turned on. 17 | Note: Be careful as "accept_ra" may need to be set to 2 on the 18 | interface during testing for the routing tables to retain their 19 | default route (unrelated to this patch but took me a while to 20 | discover). 21 | 22 | * When a session ends then anything that was "autowired" will be 23 | automatically removed thus ensuring the routing tables are in a 24 | similar state to before the daemon (or session) made any changes 25 | 26 | * Added a feature where the session will attempt to renew itself 27 | (with a new NDP Solicitation) before it self-terminates, this is 28 | required otherwise packets could be lost when the session terminates 29 | triggering the automatically removal of the route table entry. 30 | 31 | * Ensured that renew operations only take place if the session has 32 | been recently touched by an external solicitation - this ensures 33 | that sessions that become IDLE are cleaned up quickly 34 | 35 | * Moved the daemonizing step till after the system executed the 36 | configure step so that the error exit codes are returned to the daemon 37 | caller. 38 | 39 | * No longer continuing to load the daemon if any of the interfaces fail 40 | to load which should give a more predictable behaviour and better user experience. 41 | 42 | 2016-04-18 Daniel Adolfsson 43 | 44 | * Version 0.2.5 45 | 46 | * Defer configuration of interfaces until after daemonized; fixes an 47 | issue where ndppd would fail to set ALLMULTI on the interface 48 | properly. 49 | 50 | * Fix a cast so ndppd can be compiled on GCC 6. 51 | 52 | * Fix so ndppd changes working directory to / and umask to 0 once 53 | daemonized. 54 | 55 | 2015-10-13 Daniel Adolfsson 56 | 57 | * Version 0.2.4 58 | 59 | * Fix an issue where ndppd daemonizes too early. 60 | 61 | * Fix to make sure the right pid is written to the pidfile. 62 | 63 | 2012-09-21 Daniel Adolfsson 64 | 65 | * Version 0.2.3 66 | 67 | 2012-02-06 Daniel Adolfsson 68 | 69 | * Version 0.2.2 70 | 71 | * Removed "libconfuse" dependency. 72 | 73 | * New "auto" configuration to detect outgoing interface, for forwarding 74 | Neighbor Solicitation Messages. 75 | 76 | * Improved logging. 77 | 78 | * Bug fixes related to memory management. 79 | 80 | 2012-01-26 Daniel Adolfsson 81 | 82 | * Author changed e-mail address; updated copyright info. 83 | 84 | 2011-10-11 Daniel Adolfsson 85 | 86 | * Initial Release; 0.2.1 87 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ifdef DEBUG 2 | CXXFLAGS ?= -g -DDEBUG 3 | else 4 | CXXFLAGS ?= -O3 5 | endif 6 | 7 | PREFIX ?= /usr/local 8 | CXX ?= g++ 9 | GZIP ?= /bin/gzip 10 | MANDIR ?= ${DESTDIR}${PREFIX}/share/man 11 | SBINDIR ?= ${DESTDIR}${PREFIX}/sbin 12 | PKG_CONFIG ?= pkg-config 13 | 14 | 15 | OBJS = src/logger.o src/ndppd.o src/iface.o src/proxy.o src/address.o \ 16 | src/rule.o src/session.o src/conf.o src/route.o 17 | 18 | ifdef WITH_ND_NETLINK 19 | LIBS = `${PKG_CONFIG} --libs glib-2.0 libnl-3.0 libnl-route-3.0` -pthread 20 | CPPFLAGS = `${PKG_CONFIG} --cflags glib-2.0 libnl-3.0 libnl-route-3.0` 21 | OBJ = ${OBJ} src/nd-netlink.o 22 | endif 23 | 24 | all: ndppd ndppd.1.gz ndppd.conf.5.gz 25 | 26 | install: all 27 | mkdir -p ${SBINDIR} ${MANDIR} ${MANDIR}/man1 ${MANDIR}/man5 28 | cp ndppd ${SBINDIR} 29 | chmod +x ${SBINDIR}/ndppd 30 | cp ndppd.1.gz ${MANDIR}/man1 31 | cp ndppd.conf.5.gz ${MANDIR}/man5 32 | 33 | ndppd.1.gz: 34 | ${GZIP} < ndppd.1 > ndppd.1.gz 35 | 36 | ndppd.conf.5.gz: 37 | ${GZIP} < ndppd.conf.5 > ndppd.conf.5.gz 38 | 39 | ndppd: ${OBJS} 40 | ${CXX} -o ndppd ${LDFLAGS} ${OBJS} ${LIBS} 41 | 42 | nd-proxy: nd-proxy.c 43 | ${CXX} -o nd-proxy -Wall -Werror ${LDFLAGS} `${PKG_CONFIG} --cflags glib-2.0` nd-proxy.c `${PKG_CONFIG} --libs glib-2.0` 44 | 45 | .cc.o: 46 | ${CXX} -c ${CPPFLAGS} $(CXXFLAGS) -o $@ $< 47 | 48 | clean: 49 | rm -f ndppd ndppd.conf.5.gz ndppd.1.gz ${OBJS} nd-proxy 50 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | ndppd - NDP Proxy Daemon 2 | 3 | Version 0.2.5 4 | 5 | ------------------------------------------------------------------------ 6 | 1. Legal 7 | ------------------------------------------------------------------------ 8 | 9 | ndppd - NDP Proxy Daemon 10 | Copyright (C) 2011-2016 Daniel Adolfsson 11 | 12 | This program 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 | This program 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 this program. If not, see . 24 | 25 | ------------------------------------------------------------------------ 26 | 2. About 'ndppd' 27 | ------------------------------------------------------------------------ 28 | 29 | 'ndppd', or NDP Proxy Daemon, is a daemon that proxies NDP (Neighbor 30 | Discovery Protocol) messages between interfaces. 31 | 32 | The Neighbor Discovery Protocol (NDP) is a protocol in the Internet 33 | Protocol Suite used with Internet Protocol Version 6 (IPv6). It 34 | operates in the Link Layer of the Internet model (RFC 1122) and is 35 | responsible for address autoconfiguration of nodes, discovery of 36 | other nodes on the link, determining the Link Layer addresses 37 | of other nodes, duplicate address detection, finding available 38 | routers and Domain Name System (DNS) servers, address prefix 39 | discovery, and maintaining reachability information about the paths 40 | to other active neighbor nodes (RFC 4861). (Wikipedia) 41 | 42 | 'ndppd' currently only supports Neighbor Solicitation Messages and 43 | Neighbor Advertisement Messages. 44 | 45 | Before an IPv6 packet can be sent to a host, that host's link-layer 46 | address must first be discovered. This is done by sending a Neighbor 47 | Solicitation message containing the requested target IPv6 address 48 | to a specific multicast address. If a host have configured a 49 | matching IP, that host will then respond with a Neighbor 50 | Advertisement message, and provide it's link-layer address. 51 | 52 | Let's say you want to route some IPs to another interface, and 53 | your ISP isn't truly routing your subnet to your host. It means 54 | that your host will have respond to Neighbor Solicitation messages 55 | for IPs it haven't configured in order to be able to route them. 56 | 57 | Linux have a limited support for proxying Neighbor Solicitation 58 | messages by simply answering to any messages where the target IP 59 | can be found in the host's neighbor proxy table. To make this work 60 | you need to enable "proxy_ndp", and then add each single host to the 61 | neighbor proxy table by typing something like: 62 | 63 | ip -6 neigh add proxy dev 64 | 65 | Unfortunately, it doesn't support listing proxies, and as I said, 66 | only individual IPs are supported. No subnets. 67 | 68 | 'ndppd' solves this by listening for Neighbor Solicitation messages 69 | on an interface, then query the internal interfaces for that target 70 | IP before finally sending a Neighbor Advertisement message. 71 | 72 | You can create rules to query one interface for one subnet, and 73 | another interface for another. 'ndppd' can even respond directly to 74 | Neighbor Solicitation messages without querying anything, should you 75 | need that. 76 | 77 | ------------------------------------------------------------------------ 78 | 3. Dependencies 79 | ------------------------------------------------------------------------ 80 | 81 | As of version 0.2.2, libconfuse is no longer needed. 82 | 83 | ------------------------------------------------------------------------ 84 | 4. Compiling 85 | ------------------------------------------------------------------------ 86 | 87 | First, make sure you have g++ and make installed. 88 | 89 | It should be as easy as: 90 | 91 | make all && make install 92 | 93 | If you want to enable debugging, you can type: 94 | 95 | make DEBUG=1 all 96 | 97 | Note that this version of the binary is much bigger, and the daemon 98 | produces a lot of messages. 99 | 100 | ------------------------------------------------------------------------ 101 | 5. Usage 102 | ------------------------------------------------------------------------ 103 | 104 | Read through 'ndppd.conf-dist' for guidelines and examples how to 105 | configure the daemon. 106 | 107 | Usage: ndppd [-d] [-c ] [-p ] 108 | 109 | -p 110 | Create a pidfile at the specified location. 111 | 112 | -c 113 | Read configuration from the specified location, instead of 114 | the default which is /etc/ndppd.conf. 115 | 116 | -d Daemonize the process, putting it in the background. 117 | Also enables syslogging. 118 | 119 | -v Increase logging verbosity. Can be used several times in 120 | order to increase even further. 121 | 122 | ------------------------------------------------------------------------ 123 | 5. Website and contact 124 | ------------------------------------------------------------------------ 125 | 126 | Contact : Daniel Adolfsson 127 | 128 | Website : https://github.com/DanielAdolfsson/ndppd 129 | 130 | Git : git://github.com/DanielAdolfsson/ndppd.git 131 | 132 | If you want to report a bug, you can either send me a mail directly, 133 | or submit an issue on github.com. 134 | 135 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NDPPD 2 | 3 | ***ndppd***, or ***NDP Proxy Daemon***, is a daemon that proxies *neighbor discovery* messages. It listens for *neighbor solicitations* on a 4 | specified interface and responds with *neighbor advertisements* - as described in **RFC 4861** (section 7.2). 5 | 6 | ## Current status 7 | 8 | Version 0.x is in maintenance, and is being replaced by `1.0-devel` which you can find [here](https://github.com/DanielAdolfsson/ndppd/tree/1.0-devel). `1.0` is not yet stable enough to be used in production, and I currently have no estimate when it is. Feel free to try it out if you like. 9 | 10 | Latest stable release is 0.2.5: 11 | - [Download](https://github.com/DanielAdolfsson/ndppd/releases/tag/0.2.5) 12 | - [README](https://github.com/DanielAdolfsson/ndppd/blob/0.2.5/README) 13 | -------------------------------------------------------------------------------- /nd-proxy.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file nd-proxy.c 3 | * 4 | * Copyright 2016, Allied Telesis Labs New Zealand, Ltd 5 | * 6 | * +---------+ 7 | * If A | | If B 8 | * A-----------------| PROXY |-----------------B 9 | * | | 10 | * +---------+ 11 | * IPv6: A IPv6: PA IPv6: PB IPv6: B 12 | * L2: a L2: pa L2: pb L2: b 13 | * 14 | * RS/RA proxy 15 | * RS 16 | * --------------------> 17 | * L3src=A, L3dst=AllR L3src=A, L3dst=AllR 18 | * L2src=a, L2dst=allr, SLL=a L2src=pb, L2dst=allr, SLL=pb 19 | * 20 | * RA 21 | * <-------------------- 22 | * L3src=B, L3dst=AllN L3src=B, L3dst=AllN 23 | * L2src=pa, L2dst=alln, SLL=pa L2src=b, L2dst=alln, SLL=b 24 | * 25 | * This program is free software: you can redistribute it and/or modify 26 | * it under the terms of the GNU General Public License as published by 27 | * the Free Software Foundation, either version 3 of the License, or 28 | * (at your option) any later version. 29 | * 30 | * This program is distributed in the hope that it will be useful, 31 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 32 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 33 | * GNU General Public License for more details. 34 | * 35 | * You should have received a copy of the GNU General Public License 36 | * along with this program. If not, see . 37 | */ 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include 52 | 53 | #include 54 | #include 55 | #include 56 | #include 57 | #include 58 | 59 | #include 60 | 61 | /* Mode */ 62 | #define PROXY_RS (1 << 0) 63 | #define PROXY_RA (1 << 1) 64 | #define PROXY_NS (1 << 2) 65 | #define PROXY_NA (1 << 3) 66 | #define PROXY_RD (1 << 4) 67 | 68 | /* Debug macros */ 69 | #define DEBUG(fmt, args...) if (debug) printf (fmt, ## args) 70 | #define ERROR(fmt, args...) \ 71 | { \ 72 | syslog(LOG_ERR, fmt, ## args); \ 73 | fprintf(stderr, fmt, ## args); \ 74 | } 75 | 76 | /* Proxy interface */ 77 | typedef struct _iface_t { 78 | char *name; 79 | uint32_t flags; 80 | int ifindex; 81 | uint8_t hwaddr[ETH_ALEN]; 82 | int fd; 83 | guint src; 84 | } iface_t; 85 | 86 | /* Globals */ 87 | static bool debug = false; 88 | static GList *ifaces = NULL; 89 | 90 | /* Find the specified option in the ICMPv6 message */ 91 | static struct nd_opt_hdr * 92 | find_option(struct icmp6_hdr *icmp6_hdr, int len, uint8_t type) 93 | { 94 | struct nd_opt_hdr *nd_opt; 95 | int icmp_hlen; 96 | 97 | /* Each ND type has a different offest to the options */ 98 | switch (icmp6_hdr->icmp6_type) { 99 | case ND_ROUTER_SOLICIT: 100 | icmp_hlen = sizeof(struct nd_router_solicit); 101 | break; 102 | case ND_ROUTER_ADVERT: 103 | icmp_hlen = sizeof(struct nd_router_advert); 104 | break; 105 | case ND_NEIGHBOR_SOLICIT: 106 | icmp_hlen = sizeof(struct nd_neighbor_solicit); 107 | break; 108 | case ND_NEIGHBOR_ADVERT: 109 | icmp_hlen = sizeof(struct nd_neighbor_advert); 110 | break; 111 | case ND_REDIRECT: 112 | icmp_hlen = sizeof(struct nd_redirect); 113 | break; 114 | default: 115 | return NULL; 116 | } 117 | 118 | /* Find the option */ 119 | nd_opt = (struct nd_opt_hdr *)((uint8_t *)icmp6_hdr + icmp_hlen); 120 | len -= icmp_hlen; 121 | while (len > 0) { 122 | int opt_len = nd_opt->nd_opt_len * 8; 123 | if (nd_opt->nd_opt_type == type) 124 | return nd_opt; 125 | nd_opt = (struct nd_opt_hdr *)((uint8_t *)nd_opt + 126 | sizeof(struct nd_opt_hdr) + opt_len); 127 | len -= (sizeof(struct nd_opt_hdr) + opt_len); 128 | } 129 | return NULL; 130 | } 131 | 132 | /* Update the SLLA option in the packet (and checksum) */ 133 | static void 134 | update_slla_option(struct icmp6_hdr *icmp6_hdr, int len, uint8_t *mac) 135 | { 136 | struct nd_opt_hdr *nd_opt; 137 | 138 | /* Find the "source link-layer address" option */ 139 | nd_opt = find_option(icmp6_hdr, len, ND_OPT_SOURCE_LINKADDR); 140 | 141 | /* Update the slla if we found it */ 142 | if (nd_opt) { 143 | /* Option data is the mac address - it is always 16-bit aligned */ 144 | uint8_t *slla = (uint8_t *)nd_opt + sizeof(struct nd_opt_hdr); 145 | 146 | /* Update ICMPv6 header checksum based on the old and new mac adddress */ 147 | uint16_t *omac = (uint16_t *)slla; 148 | uint16_t *nmac = (uint16_t *)mac; 149 | int i; 150 | for (i = 0; i < ETH_ALEN / 2; i++) { 151 | uint16_t hc_complement = ~ntohs(icmp6_hdr->icmp6_cksum); 152 | uint16_t m_complement = ~ntohs(omac[i]); 153 | uint16_t m_prime = ntohs(nmac[i]); 154 | uint32_t sum = hc_complement + m_complement + m_prime; 155 | while (sum >> 16) { 156 | sum = (sum & 0xffff) + (sum >> 16); 157 | } 158 | icmp6_hdr->icmp6_cksum = htons(~((uint16_t)sum)); 159 | } 160 | 161 | /* Copy the outgoing interface's hw addr into the 162 | * "source link-layer address" option in the pkt. */ 163 | memcpy(slla, mac, ETH_ALEN); 164 | } 165 | } 166 | 167 | /* Proxying of both RS and RA */ 168 | static void 169 | proxy_rsra(iface_t *iface, uint8_t *msg, int len) 170 | { 171 | struct ether_header *eth_hdr; 172 | struct ip6_hdr *ip6; 173 | struct icmp6_hdr *icmp6_hdr; 174 | struct sockaddr_ll socket_address; 175 | 176 | /* Parse the packet */ 177 | eth_hdr = (struct ether_header *)msg; 178 | ip6 = (struct ip6_hdr *)(msg + sizeof(struct ether_header)); 179 | icmp6_hdr = (struct icmp6_hdr *)(msg + sizeof(struct ether_header) + 180 | sizeof(struct ip6_hdr)); 181 | 182 | DEBUG("Tx(%s): %s\n", iface->name, 183 | icmp6_hdr->icmp6_type == ND_ROUTER_SOLICIT ? 184 | "ND_ROUTER_SOLICIT" : "ND_ROUTER_ADVERT"); 185 | 186 | /* Avoid proxying spoofed packets */ 187 | if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_src)) { 188 | DEBUG("Tx(%s): Ignoring RS/RA from spoofed address\n", iface->name); 189 | return; 190 | } 191 | 192 | /* RS should be sent to "All Routers Address" FF02::2 */ 193 | /* RA should be sent to "All Nodes Address" FF02::1 */ 194 | /* Can only proxy to multicast L2 destinations 33:33:.. */ 195 | if (!IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) || 196 | (eth_hdr->ether_dhost[0] != 0x33 && eth_hdr->ether_dhost[1] != 0x33)) { 197 | DEBUG("Tx(%s): Ignoring RS/RA to non-multicast address\n", iface->name); 198 | return; 199 | } 200 | 201 | /* Copy the outgoing interface's hw addr into the 202 | * "source link-layer address" option in the pkt */ 203 | update_slla_option(icmp6_hdr, len - ((uint8_t *)icmp6_hdr - msg), iface->hwaddr); 204 | 205 | /* Copy the outgoing interface's hw addr into the 206 | * MAC source address in the pkt. */ 207 | memcpy((uint8_t *)(eth_hdr->ether_shost), iface->hwaddr, ETH_ALEN); 208 | 209 | /* Send the packet */ 210 | socket_address.sll_ifindex = iface->ifindex; 211 | socket_address.sll_halen = ETH_ALEN; 212 | memcpy((uint8_t *)socket_address.sll_addr, iface->hwaddr, ETH_ALEN); 213 | if (sendto (iface->fd, msg, len, 0, (struct sockaddr *)&socket_address, 214 | sizeof(struct sockaddr_ll)) < 0) { 215 | ERROR("Tx(%s): Failed to send packet\n", iface->name); 216 | return; 217 | } 218 | } 219 | 220 | static gboolean 221 | handle_fd(gint fd, GIOCondition condition, gpointer data) 222 | { 223 | iface_t *iface = (iface_t *)data; 224 | struct msghdr mhdr; 225 | struct iovec iov; 226 | struct sockaddr_ll t_saddr; 227 | uint8_t msg[4096]; 228 | int size = 4096; 229 | int len; 230 | 231 | /* Receive a packet */ 232 | iov.iov_len = size; 233 | iov.iov_base = (caddr_t)msg; 234 | memset(&mhdr, 0, sizeof(mhdr)); 235 | mhdr.msg_name = (caddr_t)&t_saddr; 236 | mhdr.msg_namelen = sizeof(struct sockaddr); 237 | mhdr.msg_iov = &iov; 238 | mhdr.msg_iovlen = 1; 239 | if ((len = recvmsg(fd, &mhdr, 0)) < 0) { 240 | DEBUG("Rx(%s):Interface has gone away\n", iface->name); 241 | return true; 242 | } 243 | 244 | /* Check we have at least the icmp header */ 245 | if ((size_t) len < (ETH_HLEN + sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr))) { 246 | ERROR("Rx(%s): Ignoring short packet (%d bytes)\n", iface->name, len); 247 | return true; 248 | } 249 | 250 | struct icmp6_hdr *icmp6_hdr = (struct icmp6_hdr *)(msg + ETH_HLEN + sizeof(struct ip6_hdr)); 251 | uint8_t icmp6_type = icmp6_hdr->icmp6_type; 252 | switch (icmp6_type) { 253 | case ND_ROUTER_SOLICIT: 254 | DEBUG("Rx(%s): ND_ROUTER_SOLICIT\n", iface->name); 255 | if (iface->flags & PROXY_RS) { 256 | GList *iter; 257 | for (iter = ifaces; iter; iter = g_list_next(iter)) { 258 | iface_t *oiface = (iface_t *)iter->data; 259 | if (oiface != iface) 260 | proxy_rsra(oiface, msg, len); 261 | } 262 | } 263 | break; 264 | case ND_ROUTER_ADVERT: 265 | DEBUG("Rx(%s): ND_ROUTER_ADVERT\n", iface->name); 266 | if (iface->flags & PROXY_RA) { 267 | GList *iter; 268 | for (iter = ifaces; iter; iter = g_list_next(iter)) { 269 | iface_t *oiface = (iface_t *)iter->data; 270 | if (oiface != iface) 271 | proxy_rsra(oiface, msg, len); 272 | } 273 | } 274 | break; 275 | case ND_NEIGHBOR_SOLICIT: 276 | case ND_NEIGHBOR_ADVERT: 277 | case ND_REDIRECT: 278 | default: 279 | DEBUG("Rx(%s): ignoring ICMPv6 packets of type %d\n", iface->name, icmp6_type); 280 | break; 281 | } 282 | 283 | return true; 284 | } 285 | 286 | static char *flags_to_string(uint32_t flags) 287 | { 288 | static char sbuffer[256]; 289 | sbuffer[0] = '\0'; 290 | if (flags == 0) 291 | sprintf(sbuffer, "no packets"); 292 | if (flags & PROXY_NS) 293 | sprintf(sbuffer + strlen(sbuffer), "%sNS", 294 | strlen(sbuffer) ? "," : ""); 295 | if (flags & PROXY_NA) 296 | sprintf(sbuffer + strlen(sbuffer), "%sNA", 297 | strlen(sbuffer) ? "," : ""); 298 | if (flags & PROXY_RS) 299 | sprintf(sbuffer + strlen(sbuffer), "%sRS", 300 | strlen(sbuffer) ? "," : ""); 301 | if (flags & PROXY_RA) 302 | sprintf(sbuffer + strlen(sbuffer), "%sRA", 303 | strlen(sbuffer) ? "," : ""); 304 | if (flags & PROXY_RD) 305 | sprintf(sbuffer + strlen(sbuffer), "%sRD", 306 | strlen(sbuffer) ? "," : ""); 307 | return sbuffer; 308 | } 309 | 310 | static struct sock_filter bpf_filter[] = { 311 | /* Load the ether_type. */ 312 | BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 313 | offsetof(struct ether_header, ether_type)), 314 | /* Bail if it's* not* ETHERTYPE_IPV6. */ 315 | BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ETHERTYPE_IPV6, 0, 9), 316 | /* Load the next header type. */ 317 | BPF_STMT(BPF_LD | BPF_B | BPF_ABS, 318 | sizeof(struct ether_header) + offsetof(struct ip6_hdr, 319 | ip6_nxt)), 320 | /* Bail if it's* not* IPPROTO_ICMPV6. */ 321 | BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, IPPROTO_ICMPV6, 0, 7), 322 | /* Load the ICMPv6 type. */ 323 | BPF_STMT(BPF_LD | BPF_B | BPF_ABS, 324 | sizeof(struct ether_header) + sizeof(struct ip6_hdr) + 325 | offsetof(struct icmp6_hdr, icmp6_type)), 326 | /* Bail if it's* not* ND */ 327 | BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ND_ROUTER_SOLICIT, 4, 0), 328 | BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ND_ROUTER_ADVERT, 3, 0), 329 | BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ND_NEIGHBOR_SOLICIT, 2, 0), 330 | BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ND_NEIGHBOR_ADVERT, 1, 0), 331 | BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ND_REDIRECT, 0, 1), 332 | /* Keep packet. */ 333 | BPF_STMT(BPF_RET | BPF_K, (u_int32_t)-1), 334 | /* Drop packet. */ 335 | BPF_STMT(BPF_RET | BPF_K, 0) 336 | }; 337 | 338 | static void iface_open(gpointer data, gpointer user) 339 | { 340 | iface_t *iface = (iface_t *)data; 341 | struct sockaddr_ll lladdr; 342 | struct sock_fprog fprog; 343 | struct ifreq ifr; 344 | int on = 1; 345 | int fd; 346 | 347 | DEBUG("Open(%s): %s\n", iface->name, flags_to_string(iface->flags)); 348 | 349 | /* Check the interface exists by getting its ifindex */ 350 | iface->ifindex = if_nametoindex(iface->name); 351 | if (!iface->ifindex) { 352 | ERROR("Open(%s): Could not find interface\n", iface->name); 353 | exit(-1); 354 | } 355 | 356 | /* Create raw socket for tx/rx of IPv6 packets */ 357 | if ((fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IPV6))) < 0) { 358 | ERROR("Open(%s): Unable to create socket\n", iface->name); 359 | exit(-1); 360 | } 361 | 362 | /* Bind the socket to the specified interface */ 363 | memset(&lladdr, 0, sizeof(struct sockaddr_ll)); 364 | lladdr.sll_family = AF_PACKET; 365 | lladdr.sll_protocol = htons(ETH_P_IPV6); 366 | lladdr.sll_ifindex = iface->ifindex; 367 | if (bind(fd, (struct sockaddr *)&lladdr, sizeof(struct sockaddr_ll)) < 0) { 368 | close(fd); 369 | ERROR("Open(%s): Failed to bind to interface\n", iface->name); 370 | exit(-1); 371 | } 372 | 373 | /* Set the socket non-blocking */ 374 | if (ioctl(fd, FIONBIO, (char *)&on) < 0) { 375 | close(fd); 376 | ERROR("Open(%s): Failed to make interface non-blocking\n", iface->name); 377 | exit(-1); 378 | } 379 | 380 | /* Setup a filter to only receive ND packets */ 381 | fprog.len = sizeof(bpf_filter) / sizeof(bpf_filter[0]); 382 | fprog.filter = bpf_filter; 383 | if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog)) < 0) { 384 | close(fd); 385 | ERROR("Open(%s): Failed to set filter for ND packets\n", iface->name); 386 | exit(-1); 387 | } 388 | 389 | /* Enable all multicast for this interface */ 390 | memset(&ifr, 0, sizeof(struct ifreq)); 391 | strncpy(ifr.ifr_name, iface->name, IFNAMSIZ - 1); 392 | if (ioctl(fd, SIOCGIFFLAGS, &ifr) < 0) { 393 | close(fd); 394 | ERROR("Open(%s): Failed to get flags for interface\n", iface->name); 395 | exit(-1); 396 | } 397 | ifr.ifr_flags |= IFF_ALLMULTI; 398 | if (ioctl(fd, SIOCSIFFLAGS, &ifr) < 0) { 399 | close(fd); 400 | ERROR("Open(%s): Failed to set flags for interface\n", iface->name); 401 | exit(-1); 402 | } 403 | 404 | /* Get the hwaddr of the interface */ 405 | memset(&ifr, 0, sizeof(struct ifreq)); 406 | strncpy(ifr.ifr_name, iface->name, IFNAMSIZ - 1); 407 | if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0) { 408 | ERROR("Open(%s): Failed to get interface hwaddr\n", iface->name); 409 | exit(-1); 410 | } 411 | memcpy(iface->hwaddr, ifr.ifr_hwaddr.sa_data, ETH_ALEN); 412 | 413 | /* Watch for packets */ 414 | iface->fd = fd; 415 | iface->src = g_unix_fd_add(fd, G_IO_IN, handle_fd, iface); 416 | } 417 | 418 | static void iface_close(gpointer data) 419 | { 420 | iface_t *iface = (iface_t *)data; 421 | DEBUG("Close(%s)\n", iface->name); 422 | g_source_remove(iface->src); 423 | close(iface->fd); 424 | free(iface->name); 425 | free(iface); 426 | } 427 | 428 | static iface_t *parse_interface(char *desc) 429 | { 430 | char *name = NULL; 431 | uint32_t flags = PROXY_RS | PROXY_RA; 432 | char *pflags = strchr(desc, ':'); 433 | 434 | if (pflags) { 435 | char *token = strtok(pflags + 1, ","); 436 | flags = 0; 437 | while (token != NULL) { 438 | if (strcmp("NS", token) == 0) 439 | flags |= PROXY_NS; 440 | else if (strcmp("NA", token) == 0) 441 | flags |= PROXY_NA; 442 | else if (strcmp("RA", token) == 0) 443 | flags |= PROXY_RA; 444 | else if (strcmp("RS", token) == 0) 445 | flags |= PROXY_RS; 446 | else if (strcmp("RD", token) == 0) 447 | flags |= PROXY_RD; 448 | else 449 | return NULL; 450 | token = strtok(NULL, ","); 451 | } 452 | name = strndup(desc, pflags - desc); 453 | } else { 454 | name = strdup(desc); 455 | } 456 | iface_t *iface = (iface_t *)g_malloc0(sizeof(iface_t)); 457 | iface->name = name; 458 | iface->flags = flags; 459 | iface->fd = -1; 460 | iface->src = 0; 461 | return iface; 462 | } 463 | 464 | static gboolean termination_handler(gpointer arg1) 465 | { 466 | GMainLoop *loop = (GMainLoop *) arg1; 467 | g_main_loop_quit(loop); 468 | return false; 469 | } 470 | 471 | void help(char *app_name) 472 | { 473 | printf("Usage: %s [-h] [-b] [-d] -i [:[,]..]\n" 474 | " -h show this help\n" 475 | " -b background mode\n" 476 | " -d enable verbose debug\n" 477 | " -i proxy [NS,NA,RS,RA,RD] messages received on \n" 478 | "\n" "e.g %s -i eth1:RS -i eth2:RA\n", app_name, app_name); 479 | } 480 | 481 | int main(int argc, char *argv[]) 482 | { 483 | int i = 0; 484 | bool background = false; 485 | GMainLoop *loop = NULL; 486 | 487 | /* Parse options */ 488 | while ((i = getopt(argc, argv, "hdbi:")) != -1) { 489 | switch (i) { 490 | case 'd': 491 | debug = true; 492 | background = false; 493 | break; 494 | case 'b': 495 | background = true; 496 | break; 497 | case 'i': 498 | { 499 | iface_t *iface = parse_interface(optarg); 500 | if (!iface) { 501 | help(argv[0]); 502 | ERROR("ERROR: Invalid interface specification (%s)\n", optarg); 503 | return 0; 504 | } 505 | ifaces = g_list_prepend(ifaces, iface); 506 | break; 507 | } 508 | case '?': 509 | case 'h': 510 | default: 511 | help(argv[0]); 512 | return 0; 513 | } 514 | } 515 | 516 | /* Check required */ 517 | if (g_list_length(ifaces) < 2) { 518 | help(argv[0]); 519 | ERROR("ERROR: Require at least 2 interfaces.\n"); 520 | return 0; 521 | } 522 | 523 | /* Daemonize */ 524 | if (background && fork() != 0) { 525 | /* Parent */ 526 | return 0; 527 | } 528 | 529 | /* Main loop instance */ 530 | loop = g_main_loop_new(NULL, true); 531 | 532 | /* Handle SIGTERM/SIGINT/SIGPIPE gracefully */ 533 | g_unix_signal_add(SIGINT, termination_handler, loop); 534 | g_unix_signal_add(SIGTERM, termination_handler, loop); 535 | 536 | /* Startup */ 537 | g_list_foreach(ifaces, iface_open, NULL); 538 | 539 | /* Loop while not terminated */ 540 | g_main_loop_run(loop); 541 | 542 | /* Shutdown */ 543 | g_list_free_full(ifaces, iface_close); 544 | 545 | /* Free the glib main loop */ 546 | if (loop) 547 | g_main_loop_unref(loop); 548 | 549 | return 0; 550 | } 551 | -------------------------------------------------------------------------------- /ndppd-init-debian-jessi: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # kFreeBSD do not accept scripts as interpreters, using #!/bin/sh and sourcing. 3 | if [ true != "$INIT_D_SCRIPT_SOURCED" ] ; then 4 | set "$0" "$@"; INIT_D_SCRIPT_SOURCED=true . /lib/init/init-d-script 5 | fi 6 | 7 | ### BEGIN INIT INFO 8 | # Provides: ndppd 9 | # Required-Start: $remote_fs $syslog $network 10 | # Required-Stop: $remote_fs $syslog $network 11 | # Default-Start: 2 3 4 5 12 | # Default-Stop: 0 1 6 13 | # Short-Description: ndppd init script 14 | # Description: NDP Proxy Daemon init script 15 | ### END INIT INFO 16 | # Author: Torben Nehmer 17 | 18 | DESC="NDP Proxy Daemon" 19 | PIDFILE=/run/ndppd.pid 20 | DAEMON=/usr/local/sbin/ndppd 21 | DAEMON_ARGS="-d -p $PIDFILE" 22 | -------------------------------------------------------------------------------- /ndppd.1: -------------------------------------------------------------------------------- 1 | .\" Process this file with 2 | .\" groff -man -Tascii ndppd.1 3 | .\" 4 | .TH NDPPD 1 "9/18/2011" "NDP Proxy Daemon Manual" "NDP Proxy Daemon Manual" 5 | .SH NAME 6 | ndppd \- NDP Proxy Daemon 7 | .SH SYNOPSIS 8 | .B ndppd [-d] [-vvv] [-c ] [-p ] 9 | .SH DESCRIPTION 10 | .BR ndppd, 11 | or 12 | .BR "NDP Proxy Daemon" , 13 | is a daemon that proxies NDP (Neighbor Discovery Protocol) messages between interfaces. 14 | .SH OPTIONS 15 | .IP "-c " 16 | Use the alternate 17 | .I config-file 18 | instead of 19 | .IR /etc/ndppd.conf . 20 | .IP -d 21 | Daemonizes 22 | .B ndppd 23 | and puts it into the background. It also 24 | enables syslogging. 25 | .IP "-p " 26 | Creates a 27 | .I pidfile 28 | at the specified location. 29 | .IP -v 30 | Increases logging verbosity. Can be specified several times to increase 31 | verbosity even further. 32 | .SH FILES 33 | .I /etc/ndppd.conf 34 | .RS 35 | Default configuration file. See 36 | .BR ndppd.conf(5) 37 | for further details about configuration options. 38 | .RE 39 | .SH BUGS 40 | No known bugs at the time of this writing. 41 | .SH LICENSE 42 | .EX 43 | Copyright (C) 2011 Daniel Adolfsson 44 | 45 | This program is free software: you can redistribute it and/or modify 46 | it under the terms of the GNU General Public License as published by 47 | the Free Software Foundation, either version 3 of the License, or 48 | (at your option) any later version. 49 | 50 | This program is distributed in the hope that it will be useful, 51 | but WITHOUT ANY WARRANTY; without even the implied warranty of 52 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 53 | GNU General Public License for more details. 54 | 55 | You should have received a copy of the GNU General Public License 56 | along with this program. If not, see . 57 | .EE 58 | .SH AUTHOR 59 | Daniel Adolfsson 60 | .SH "SEE ALSO" 61 | .BR ndppd.conf(5) 62 | 63 | -------------------------------------------------------------------------------- /ndppd.conf-dist: -------------------------------------------------------------------------------- 1 | # route-ttl (NEW) 2 | # This tells 'ndppd' how often to reload the route file /proc/net/ipv6_route. 3 | # Default value is '30000' (30 seconds). 4 | 5 | route-ttl 30000 6 | 7 | # address-ttl (NEW) 8 | # This tells 'ndppd' how often to reload the IP address file /proc/net/if_inet6 9 | # Default value is '30000' (30 seconds). 10 | 11 | address-ttl 30000 12 | 13 | # proxy 14 | # This sets up a listener, that will listen for any Neighbor Solicitation 15 | # messages, and respond to them according to a set of rules (see below). 16 | # is required. You may have several 'proxy' sections. 17 | 18 | proxy eth0 { 19 | 20 | # router 21 | # This option turns on or off the router flag for Neighbor Advertisement 22 | # messages. Default value is 'true'. 23 | 24 | router yes 25 | 26 | # timeout 27 | # Controls how long to wait for a Neighbor Advertisment message before 28 | # invalidating the entry, in milliseconds. Default value is '500'. 29 | 30 | timeout 500 31 | 32 | # autowire 33 | # Controls whether ndppd will automatically create host entries 34 | # in the routing tables when it receives Neighbor Advertisements on a 35 | # listening interface. The the default value is no. 36 | # Note: Autowire will ignore all rules with 'auto' or 'static' given it 37 | # is expected that the routes are already defined for these paths 38 | 39 | autowire no 40 | 41 | # keepalive 42 | # Controls whether ndppd will automatically attempt to keep routing 43 | # sessions alive by actively sending out NDP Solicitations before the the 44 | # session is expired. The the default value is yes. 45 | 46 | keepalive yes 47 | 48 | # retries 49 | # Number of times a NDP Solicitation will be sent out before the daemon 50 | # considers a route unreachable. The default value is 3 51 | 52 | retries 3 53 | 54 | # promiscuous 55 | # Controls whether ndppd will put the proxy listening interface into promiscuous 56 | # mode and hence will react to inbound and outbound NDP commands. This is 57 | # required for machines behind the gateway to talk to each other in more 58 | # complex topology scenarios. The the default value is no. 59 | 60 | promiscuous no 61 | 62 | # ttl 63 | # Controls how long a valid or invalid entry remains in the cache, in 64 | # milliseconds. Default value is '30000' (30 seconds). 65 | 66 | ttl 30000 67 | 68 | # rule [/] 69 | # This is a rule that the target address is to match against. If no netmask 70 | # is provided, /128 is assumed. You may have several rule sections, and the 71 | # addresses may or may not overlap. 72 | 73 | rule 1111:: { 74 | # Only one of 'static', 'auto' and 'interface' may be specified. Please 75 | # read 'ndppd.conf' manpage for details about the methods below. 76 | 77 | # 'auto' should work in most cases. 78 | 79 | # static (NEW) 80 | # 'ndppd' will immediately answer any Neighbor Solicitation Messages 81 | # (if they match the IP rule). 82 | 83 | # iface 84 | # 'ndppd' will forward the Neighbor Solicitation Message through the 85 | # specified interface - and only respond if a matching Neighbor 86 | # Advertisement Message is received. 87 | 88 | # auto (NEW) 89 | # Same as above, but instead of manually specifying the outgoing 90 | # interface, 'ndppd' will check for a matching route in /proc/net/ipv6_route. 91 | 92 | auto 93 | 94 | # autovia 95 | # Any addresses updated using NDP advertisments will use a gateway to 96 | # route traffic on this particular interface (only works wiith the iface 97 | # rule type). Default is no 98 | 99 | autovia no 100 | 101 | # Note that before version 0.2.2 of 'ndppd', if you didn't choose a 102 | # method, it defaulted to 'static'. For compatibility reasons we choose 103 | # to keep this behavior - for now (it may be removed in a future version). 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /ndppd.conf.5: -------------------------------------------------------------------------------- 1 | .\" Process this file with 2 | .\" groff -man -Tascii ndppd.conf.5 3 | .\" 4 | .TH NDPPD\&.CONF 5 "9/18/2011" "NDP Proxy Daemon Manual" "NDP Proxy Daemon Manual" 5 | .SH NAME 6 | ndppd.conf \- configuration file for ndppd 7 | .SH DESCRIPTION 8 | The syntax is as follows: 9 | .PP 10 | .EX 11 | proxy eth0 { 12 | rule 1234:5678::/96 { 13 | } 14 | } 15 | .EE 16 | .PP 17 | The configuration file must contain one or more 18 | .B proxy 19 | sections, and each of these section must contain one or more 20 | .B rule 21 | sections. 22 | .PP 23 | The 24 | .B ndppd 25 | daemon listens on the interface specified as an argument to the 26 | .B proxy 27 | section. Once a 28 | .B Neighbor Solicitation 29 | message arrives, it will try to match the target address against 30 | the address specified as the argument of the 31 | .B rule 32 | section. 33 | .SH OPTIONS 34 | .IP "proxy " 35 | Adds a proxy and binds it to the specified 36 | .IR interface . 37 | See below for information about 38 | .BR "proxy options" . 39 | .SH PROXY OPTIONS 40 | .IP "rule
" 41 | Adds a rule with the specified 42 | .I address 43 | to the proxy. It may be a an IP such as 1234::1 or a subnet such 44 | as 1111::/96. See below for information about 45 | .BR "rule options" . 46 | .IP "ttl " 47 | Controls how long 48 | .B ndppd 49 | will cache an entry. This is in milliseconds, and the default value 50 | is 30000 (30 seconds). 51 | .IP "autowire " 52 | Controls whether 53 | .B ndppd 54 | will automatically create host entries in the routing tables when 55 | .B ndppd receives Neighbor Advertisements on a listening interface. 56 | The default value is no. 57 | .IP "promiscuous " 58 | Controls whether 59 | .B ndppd 60 | will put the proxy listening interface into promiscuous mode and 61 | hence will react to inbound and outbound NDP commands. This is 62 | required for machines behind the gateway to talk to each other in 63 | more complex topology scenarios. 64 | The the default value is no. 65 | .IP "timeout " 66 | Controls how long 67 | .B ndppd 68 | will wait for a Neighbor Advertisement message after forwarding 69 | a Neighbor Solicitation message according to the rule. This is 70 | in milliseconds, and the default value is 500 (.5 second). 71 | .IP "router " 72 | Controls if 73 | .B ndppd 74 | should send the 75 | .I router 76 | bit when sending Neighbor Advertisement messages. The default 77 | value here is 78 | .BR yes . 79 | .SH RULE OPTIONS 80 | Specify a method here. See below. 81 | .SH METHOD 82 | One of the following options must be specified in the 83 | .B rule 84 | section. All of these are mutually exclusive options, and cannot 85 | be combined. 86 | .IP "iface " 87 | Specify which 88 | .I interface 89 | the Neighbor Solicitation message will be sent out through. 90 | .IP "auto" 91 | .B (NEW) 92 | If this option is specified 93 | .B ndppd 94 | will attempt to detect which interface to use in order to forward 95 | Neighbor Solicitation Messages, by reading the routing table 96 | .BR /proc/net/ipv6_route . 97 | .IP "static" 98 | .B (NEW) 99 | This option tells 100 | .B ndppd 101 | that it should immediately respond to a Neighbor Solicitation Message 102 | without querying an internal interface. 103 | Note that it's recommended that you use this option sparingly, and with 104 | as high prefix length as possible. This is to make sure upstream routers 105 | are not polluted with spurious neighbor entries. 106 | 107 | If no rule option has been specified, it will default to 108 | .B static 109 | in order to be compatible with 110 | .BR 0.2.1 . 111 | This will, however, produce a warning, and most likely not work in 112 | future versions of 113 | .BR ndppd . 114 | .SH AUTHOR 115 | Daniel Adolfsson 116 | .SH "SEE ALSO" 117 | .BR ndppd(1) 118 | -------------------------------------------------------------------------------- /ndppd.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=NDP Proxy Daemon 3 | After=network.target 4 | 5 | [Service] 6 | ExecStart=/usr/sbin/ndppd -d -p /var/run/ndppd/ndppd.pid 7 | Type=forking 8 | PIDFile=/var/run/ndppd/ndppd.pid 9 | 10 | [Install] 11 | WantedBy=multi-user.target 12 | -------------------------------------------------------------------------------- /src/address.cc: -------------------------------------------------------------------------------- 1 | // ndppd - NDP Proxy Daemon 2 | // Copyright (C) 2011 Daniel Adolfsson 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program. If not, see . 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include 28 | #include 29 | 30 | #include "ndppd.h" 31 | #include "address.h" 32 | #include "route.h" 33 | 34 | NDPPD_NS_BEGIN 35 | 36 | std::list > address::_addresses; 37 | 38 | int address::_ttl; 39 | 40 | int address::_c_ttl; 41 | 42 | address::address() 43 | { 44 | reset(); 45 | } 46 | 47 | address::address(const address& addr) 48 | { 49 | _addr.s6_addr32[0] = addr._addr.s6_addr32[0]; 50 | _addr.s6_addr32[1] = addr._addr.s6_addr32[1]; 51 | _addr.s6_addr32[2] = addr._addr.s6_addr32[2]; 52 | _addr.s6_addr32[3] = addr._addr.s6_addr32[3]; 53 | 54 | _mask.s6_addr32[0] = addr._mask.s6_addr32[0]; 55 | _mask.s6_addr32[1] = addr._mask.s6_addr32[1]; 56 | _mask.s6_addr32[2] = addr._mask.s6_addr32[2]; 57 | _mask.s6_addr32[3] = addr._mask.s6_addr32[3]; 58 | } 59 | 60 | address::address(const ptr
& addr) 61 | { 62 | _addr.s6_addr32[0] = addr->_addr.s6_addr32[0]; 63 | _addr.s6_addr32[1] = addr->_addr.s6_addr32[1]; 64 | _addr.s6_addr32[2] = addr->_addr.s6_addr32[2]; 65 | _addr.s6_addr32[3] = addr->_addr.s6_addr32[3]; 66 | 67 | _mask.s6_addr32[0] = addr->_mask.s6_addr32[0]; 68 | _mask.s6_addr32[1] = addr->_mask.s6_addr32[1]; 69 | _mask.s6_addr32[2] = addr->_mask.s6_addr32[2]; 70 | _mask.s6_addr32[3] = addr->_mask.s6_addr32[3]; 71 | } 72 | 73 | address::address(const std::string& str) 74 | { 75 | parse_string(str); 76 | } 77 | 78 | address::address(const char* str) 79 | { 80 | parse_string(str); 81 | } 82 | 83 | address::address(const in6_addr& addr) 84 | { 85 | _addr.s6_addr32[0] = addr.s6_addr32[0]; 86 | _addr.s6_addr32[1] = addr.s6_addr32[1]; 87 | _addr.s6_addr32[2] = addr.s6_addr32[2]; 88 | _addr.s6_addr32[3] = addr.s6_addr32[3]; 89 | 90 | _mask.s6_addr32[0] = 0xffffffff; 91 | _mask.s6_addr32[1] = 0xffffffff; 92 | _mask.s6_addr32[2] = 0xffffffff; 93 | _mask.s6_addr32[3] = 0xffffffff; 94 | } 95 | 96 | address::address(const in6_addr& addr, const in6_addr& mask) 97 | { 98 | _addr.s6_addr32[0] = addr.s6_addr32[0]; 99 | _addr.s6_addr32[1] = addr.s6_addr32[1]; 100 | _addr.s6_addr32[2] = addr.s6_addr32[2]; 101 | _addr.s6_addr32[3] = addr.s6_addr32[3]; 102 | 103 | _mask.s6_addr32[0] = mask.s6_addr32[0]; 104 | _mask.s6_addr32[1] = mask.s6_addr32[1]; 105 | _mask.s6_addr32[2] = mask.s6_addr32[2]; 106 | _mask.s6_addr32[3] = mask.s6_addr32[3]; 107 | } 108 | 109 | address::address(const in6_addr& addr, int pf) 110 | { 111 | _addr.s6_addr32[0] = addr.s6_addr32[0]; 112 | _addr.s6_addr32[1] = addr.s6_addr32[1]; 113 | _addr.s6_addr32[2] = addr.s6_addr32[2]; 114 | _addr.s6_addr32[3] = addr.s6_addr32[3]; 115 | 116 | prefix(pf); 117 | } 118 | 119 | bool address::operator==(const address& addr) const 120 | { 121 | return !(((_addr.s6_addr32[0] ^ addr._addr.s6_addr32[0]) & _mask.s6_addr32[0]) | 122 | ((_addr.s6_addr32[1] ^ addr._addr.s6_addr32[1]) & _mask.s6_addr32[1]) | 123 | ((_addr.s6_addr32[2] ^ addr._addr.s6_addr32[2]) & _mask.s6_addr32[2]) | 124 | ((_addr.s6_addr32[3] ^ addr._addr.s6_addr32[3]) & _mask.s6_addr32[3])); 125 | } 126 | 127 | bool address::operator!=(const address& addr) const 128 | { 129 | return !!(((_addr.s6_addr32[0] ^ addr._addr.s6_addr32[0]) & _mask.s6_addr32[0]) | 130 | ((_addr.s6_addr32[1] ^ addr._addr.s6_addr32[1]) & _mask.s6_addr32[1]) | 131 | ((_addr.s6_addr32[2] ^ addr._addr.s6_addr32[2]) & _mask.s6_addr32[2]) | 132 | ((_addr.s6_addr32[3] ^ addr._addr.s6_addr32[3]) & _mask.s6_addr32[3])); 133 | } 134 | 135 | bool address::is_empty() const 136 | { 137 | if (_addr.s6_addr32[0] == 0 && 138 | _addr.s6_addr32[1] == 0 && 139 | _addr.s6_addr32[2] == 0 && 140 | _addr.s6_addr32[3] == 0 && 141 | _mask.s6_addr32[0] == 0xffffffff && 142 | _mask.s6_addr32[1] == 0xffffffff && 143 | _mask.s6_addr32[2] == 0xffffffff && 144 | _mask.s6_addr32[3] == 0xffffffff) 145 | return true; 146 | 147 | return false; 148 | } 149 | 150 | void address::reset() 151 | { 152 | _addr.s6_addr32[0] = 0; 153 | _addr.s6_addr32[1] = 0; 154 | _addr.s6_addr32[2] = 0; 155 | _addr.s6_addr32[3] = 0; 156 | 157 | _mask.s6_addr32[0] = 0xffffffff; 158 | _mask.s6_addr32[1] = 0xffffffff; 159 | _mask.s6_addr32[2] = 0xffffffff; 160 | _mask.s6_addr32[3] = 0xffffffff; 161 | } 162 | 163 | int address::prefix() const 164 | { 165 | if (!_mask.s6_addr[0]) { 166 | return 0; 167 | } 168 | 169 | for (int p = 0; p < 128; p++) { 170 | int byi = p / 8, bii = 7 - (p % 8); 171 | 172 | if (!(_mask.s6_addr[byi]& (1 << bii))) { 173 | return p; 174 | } 175 | } 176 | 177 | return 128; 178 | } 179 | 180 | void address::prefix(int pf) 181 | { 182 | const unsigned char maskbit[] = { 183 | 0x00, 0x80, 0xc0, 0xe0, 0xf0, 184 | 0xf8, 0xfc, 0xfe, 0xff 185 | }; 186 | 187 | if (pf >= 128) { 188 | _mask.s6_addr32[0] = 0xffffffff; 189 | _mask.s6_addr32[1] = 0xffffffff; 190 | _mask.s6_addr32[2] = 0xffffffff; 191 | _mask.s6_addr32[3] = 0xffffffff; 192 | return; 193 | } else { 194 | _mask.s6_addr32[0] = 0; 195 | _mask.s6_addr32[1] = 0; 196 | _mask.s6_addr32[2] = 0; 197 | _mask.s6_addr32[3] = 0; 198 | 199 | if (pf <= 0) { 200 | return; 201 | } 202 | } 203 | 204 | int offset = pf / 8, n; 205 | 206 | for (n = 0; n < offset; n++) { 207 | _mask.s6_addr[n] = 0xff; 208 | } 209 | 210 | _mask.s6_addr[offset] = maskbit[pf % 8]; 211 | } 212 | 213 | const std::string address::to_string() const 214 | { 215 | char buf[INET6_ADDRSTRLEN + 8]; 216 | 217 | if (!inet_ntop(AF_INET6,& _addr, buf, INET6_ADDRSTRLEN)) 218 | return "::1"; 219 | 220 | // TODO: What to do about invalid ip? 221 | 222 | int p; 223 | 224 | if ((p = prefix()) < 128) { 225 | sprintf(buf + strlen(buf), "/%d", p); 226 | } 227 | 228 | return buf; 229 | } 230 | 231 | bool address::parse_string(const std::string& str) 232 | { 233 | char buf[INET6_ADDRSTRLEN],* b; 234 | int sz; 235 | 236 | sz = 0; 237 | b = buf; 238 | 239 | reset(); 240 | 241 | const char* p = str.c_str(); 242 | 243 | while (*p && isspace(*p)) 244 | p++; 245 | 246 | while (*p) { 247 | if ((*p == '/') || isspace(*p)) { 248 | break; 249 | } 250 | 251 | if ((*p != ':') && !isxdigit(*p)) { 252 | return false; 253 | } 254 | 255 | if (sz >= (INET6_ADDRSTRLEN - 1)) { 256 | return false; 257 | } 258 | 259 | *b++ =* p++; 260 | 261 | sz++; 262 | } 263 | 264 | *b = '\0'; 265 | 266 | if (inet_pton(AF_INET6, buf,& _addr) <= 0) { 267 | return false; 268 | } 269 | 270 | while (*p && isspace(*p)) { 271 | p++; 272 | } 273 | 274 | if (*p == '\0') { 275 | _mask.s6_addr32[0] = 0xffffffff; 276 | _mask.s6_addr32[1] = 0xffffffff; 277 | _mask.s6_addr32[2] = 0xffffffff; 278 | _mask.s6_addr32[3] = 0xffffffff; 279 | return true; 280 | } 281 | 282 | if (*p++ != '/') 283 | return false; 284 | 285 | while (*p && isspace(*p)) { 286 | p++; 287 | } 288 | 289 | sz = 0; 290 | b = buf; 291 | 292 | while (*p) { 293 | if (!isdigit(*p)) { 294 | return false; 295 | } 296 | 297 | if (sz > 3) { 298 | return false; 299 | } 300 | 301 | *b++ =* p++; 302 | sz++; 303 | } 304 | 305 | *b = '\0'; 306 | 307 | prefix(atoi(buf)); 308 | 309 | return true; 310 | } 311 | 312 | address::operator std::string() const 313 | { 314 | return to_string(); 315 | } 316 | 317 | struct in6_addr& address::addr() 318 | { 319 | return _addr; 320 | } 321 | 322 | const struct in6_addr& address::const_addr() const 323 | { 324 | return _addr; 325 | } 326 | 327 | struct in6_addr& address::mask() 328 | { 329 | return _mask; 330 | } 331 | 332 | bool address::is_multicast() const 333 | { 334 | return _addr.s6_addr[0] == 0xff; 335 | } 336 | 337 | bool address::is_unicast() const 338 | { 339 | if (_addr.s6_addr32[2] == 0 && 340 | _addr.s6_addr32[3] == 0) 341 | return false; 342 | 343 | return _addr.s6_addr[0] != 0xff; 344 | } 345 | 346 | void address::add(const address& addr, const std::string& ifname) 347 | { 348 | ptr rt(new route(addr, ifname)); 349 | // logger::debug() << "address::create() addr=" << addr << ", ifname=" << ifname; 350 | _addresses.push_back(rt); 351 | } 352 | 353 | std::list >::iterator address::addresses_begin() 354 | { 355 | return _addresses.begin(); 356 | } 357 | 358 | std::list >::iterator address::addresses_end() 359 | { 360 | return _addresses.end(); 361 | } 362 | 363 | void address::load(const std::string& path) 364 | { 365 | // Hack to make sure the addresses are not freed prematurely. 366 | std::list > tmp_addresses(_addresses); 367 | _addresses.clear(); 368 | 369 | logger::debug() << "reading IP addresses"; 370 | 371 | try { 372 | std::ifstream ifs; 373 | ifs.exceptions(std::ifstream::badbit | std::ifstream::failbit); 374 | ifs.open(path.c_str(), std::ios::in); 375 | ifs.exceptions(std::ifstream::badbit); 376 | 377 | while (!ifs.eof()) { 378 | char buf[1024]; 379 | ifs.getline(buf, sizeof(buf)); 380 | 381 | if (ifs.gcount() < 53) { 382 | if (ifs.gcount() > 0) 383 | logger::debug() << "skipping entry (size=" << ifs.gcount() << ")"; 384 | continue; 385 | } 386 | 387 | address addr; 388 | 389 | if (route::hexdec(buf, (unsigned char* )&addr.addr(), 16) != 16) { 390 | logger::warning() << "failed to load address (" << buf << ")"; 391 | continue; 392 | } 393 | 394 | addr.prefix(128); 395 | 396 | std::string iface = route::token(buf + 45); 397 | 398 | address::add(addr, iface); 399 | 400 | logger::debug() << "found local addr=" << addr << ", iface=" << iface; 401 | } 402 | } catch (std::ifstream::failure e) { 403 | logger::warning() << "Failed to parse IPv6 address data from '" << path << "'"; 404 | logger::error() << e.what(); 405 | } 406 | 407 | logger::debug() << "completed IP addresses load"; 408 | } 409 | 410 | void address::update(int elapsed_time) 411 | { 412 | if ((_c_ttl -= elapsed_time) <= 0) { 413 | load("/proc/net/if_inet6"); 414 | _c_ttl = _ttl; 415 | } 416 | } 417 | 418 | int address::ttl() 419 | { 420 | return _ttl; 421 | } 422 | 423 | void address::ttl(int ttl) 424 | { 425 | _ttl = ttl; 426 | } 427 | 428 | NDPPD_NS_END 429 | -------------------------------------------------------------------------------- /src/address.h: -------------------------------------------------------------------------------- 1 | // ndppd - NDP Proxy Daemon 2 | // Copyright (C) 2011 Daniel Adolfsson 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program. If not, see . 16 | #pragma once 17 | 18 | #include 19 | #include 20 | #include 21 | 22 | #include "ndppd.h" 23 | 24 | NDPPD_NS_BEGIN 25 | 26 | class iface; 27 | 28 | class route; 29 | 30 | class address { 31 | public: 32 | address(); 33 | address(const address& addr); 34 | address(const ptr
& addr); 35 | address(const std::string& str); 36 | address(const char* str); 37 | address(const in6_addr& addr); 38 | address(const in6_addr& addr, const in6_addr& mask); 39 | address(const in6_addr& addr, int prefix); 40 | 41 | static void update(int elapsed_time); 42 | 43 | static int ttl(); 44 | 45 | static void ttl(int ttl); 46 | 47 | struct in6_addr& addr(); 48 | 49 | const struct in6_addr& const_addr() const; 50 | 51 | struct in6_addr& mask(); 52 | 53 | // Compare _a/_m against a._a. 54 | bool operator==(const address& addr) const; 55 | 56 | bool operator!=(const address& addr) const; 57 | 58 | void reset(); 59 | 60 | bool is_empty() const; 61 | 62 | const std::string to_string() const; 63 | 64 | bool parse_string(const std::string& str); 65 | 66 | int prefix() const; 67 | 68 | void prefix(int n); 69 | 70 | bool is_unicast() const; 71 | 72 | bool is_multicast() const; 73 | 74 | operator std::string() const; 75 | 76 | static void add(const address& addr, const std::string& ifname); 77 | 78 | static void load(const std::string& path); 79 | 80 | static std::list >::iterator addresses_begin(); 81 | 82 | static std::list >::iterator addresses_end(); 83 | 84 | private: 85 | static int _ttl; 86 | 87 | static int _c_ttl; 88 | 89 | static std::list > _addresses; 90 | 91 | struct in6_addr _addr, _mask; 92 | }; 93 | 94 | NDPPD_NS_END 95 | -------------------------------------------------------------------------------- /src/conf.cc: -------------------------------------------------------------------------------- 1 | // ndppd - NDP Proxy Daemon 2 | // Copyright (C) 2011 Daniel Adolfsson 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program. If not, see . 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include "ndppd.h" 28 | 29 | NDPPD_NS_BEGIN 30 | 31 | conf::conf() : 32 | _is_block(false) 33 | { 34 | 35 | } 36 | 37 | conf::operator int() const 38 | { 39 | return as_int(); 40 | } 41 | 42 | conf::operator const std::string&() const 43 | { 44 | return as_str(); 45 | } 46 | 47 | conf::operator bool() const 48 | { 49 | return as_bool(); 50 | } 51 | 52 | bool conf::as_bool() const 53 | { 54 | if (!strcasecmp(_value.c_str(), "true") || !strcasecmp(_value.c_str(), "yes")) 55 | return true; 56 | else 57 | return false; 58 | } 59 | 60 | const std::string& conf::as_str() const 61 | { 62 | return _value; 63 | } 64 | 65 | int conf::as_int() const 66 | { 67 | return atoi(_value.c_str()); 68 | } 69 | 70 | bool conf::empty() const 71 | { 72 | return _value == ""; 73 | } 74 | 75 | ptr conf::load(const std::string& path) 76 | { 77 | try { 78 | std::ifstream ifs; 79 | ifs.exceptions(std::ifstream::failbit | std::ifstream::badbit); 80 | ifs.open(path.c_str(), std::ios::in); 81 | ifs.exceptions(std::ifstream::badbit); 82 | std::string buf((std::istreambuf_iterator(ifs)), std::istreambuf_iterator()); 83 | 84 | const char* c_buf = buf.c_str(); 85 | 86 | ptr cf(new conf); 87 | 88 | if (cf->parse_block(&c_buf)) { 89 | cf->dump(LOG_DEBUG); 90 | return cf; 91 | } 92 | 93 | logger::error() << "Could not parse configuration file"; 94 | } catch (std::ifstream::failure e) { 95 | logger::error() << "Failed to load configuration file '" << path << "'"; 96 | } 97 | 98 | return ptr(); 99 | } 100 | 101 | bool conf::is_block() const 102 | { 103 | return _is_block; 104 | } 105 | 106 | const char* conf::skip(const char* str, bool newlines) 107 | { 108 | while (*str) { 109 | while (*str && isspace(*str) && ((*str != '\n') || newlines)) 110 | str++; 111 | 112 | if ((*str == '#') || ((*str == '/') && (*(str + 1) == '/'))) { 113 | while (*str && (*str != '\n')) { 114 | str++; 115 | } 116 | } else if ((*str == '/') && (*(str + 1) == '*')) { 117 | while (*str) { 118 | if ((*str == '*') && (*(str + 1) == '/')) { 119 | str += 2; 120 | break; 121 | } 122 | str++; 123 | } 124 | } else { 125 | break; 126 | } 127 | } 128 | 129 | return str; 130 | } 131 | 132 | bool conf::parse_block(const char** str) 133 | { 134 | const char* p = *str; 135 | 136 | _is_block = true; 137 | 138 | while (*p) { 139 | std::stringstream ss; 140 | 141 | p = skip(p, true); 142 | 143 | if ((*p == '}') || !*p) { 144 | *str = p; 145 | return true; 146 | } 147 | 148 | while (isalnum(*p) || (*p == '_') || (*p == '-')) { 149 | ss << *p++; 150 | } 151 | 152 | p = skip(p, false); 153 | 154 | if (*p == '=') { 155 | p++; 156 | p = skip(p, false); 157 | } 158 | 159 | ptr cf(new conf); 160 | 161 | if (cf->parse(&p)) { 162 | _map.insert(std::pair >(ss.str(), cf)); 163 | } else { 164 | return false; 165 | } 166 | } 167 | 168 | *str = p; 169 | return true; 170 | } 171 | 172 | bool conf::parse(const char** str) 173 | { 174 | const char* p = *str; 175 | std::stringstream ss; 176 | 177 | p = skip(p, false); 178 | 179 | if ((*p == '\'') || (*p == '"')) { 180 | for (char e = *p++; *p && (*p != e) && (*p != '\n'); p++) 181 | ss << *p; 182 | p = skip(p, false); 183 | } else { 184 | while (*p && isgraph(*p) && (*p != '{') && (*p != '}')) { 185 | ss << *p++; 186 | } 187 | } 188 | 189 | _value = ss.str(); 190 | 191 | p = skip(p, false); 192 | 193 | if (*p == '{') { 194 | p++; 195 | 196 | if (!parse_block(&p)) { 197 | return false; 198 | } 199 | 200 | if (*p != '}') { 201 | return false; 202 | } 203 | 204 | p++; 205 | } 206 | 207 | *str = p; 208 | return true; 209 | } 210 | 211 | void conf::dump(int pri) const 212 | { 213 | logger l(pri); 214 | dump(l, 0); 215 | } 216 | 217 | void conf::dump(logger& l, int level) const 218 | { 219 | std::string pfx; 220 | for (int i = 0; i < level; i++) { 221 | pfx += " "; 222 | } 223 | 224 | if (_value != "") { 225 | l << _value << " "; 226 | } 227 | 228 | if (_is_block) { 229 | l << "{" << logger::endl; 230 | 231 | std::multimap >::const_iterator it; 232 | 233 | for (it = _map.begin(); it != _map.end(); it++) { 234 | l << pfx << " " << it->first << " "; 235 | it->second->dump(l, level + 1); 236 | } 237 | 238 | l << pfx << "}" << logger::endl; 239 | } 240 | 241 | l << logger::endl; 242 | } 243 | 244 | ptr conf::operator()(const std::string& name, int index) const 245 | { 246 | return find(name, index); 247 | } 248 | 249 | ptr conf::find(const std::string& name, int index) const 250 | { 251 | std::multimap >::const_iterator it; 252 | for (it = _map.find(name); it != _map.end(); it++) { 253 | if (index-- <= 0) 254 | return it->second; 255 | } 256 | 257 | return ptr(); 258 | } 259 | 260 | ptr conf::operator[](const std::string& name) const 261 | { 262 | return find(name, 0); 263 | } 264 | 265 | std::vector > conf::find_all(const std::string& name) const 266 | { 267 | std::vector > vec; 268 | 269 | std::multimap >::const_iterator it; 270 | 271 | std::pair >::const_iterator, 272 | std::multimap >::const_iterator> ret; 273 | 274 | ret = _map.equal_range(name); 275 | 276 | for (it = ret.first; it != ret.second; it++) { 277 | vec.push_back(it->second); 278 | } 279 | 280 | return vec; 281 | } 282 | 283 | conf::operator const std::string&() 284 | { 285 | return _value; 286 | } 287 | 288 | NDPPD_NS_END 289 | -------------------------------------------------------------------------------- /src/conf.h: -------------------------------------------------------------------------------- 1 | // ndppd - NDP Proxy Daemon 2 | // Copyright (C) 2011 Daniel Adolfsson 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program. If not, see . 16 | #pragma once 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include "ndppd.h" 25 | 26 | NDPPD_NS_BEGIN 27 | 28 | class conf { 29 | public: 30 | 31 | private: 32 | std::string _value; 33 | 34 | bool _is_block; 35 | 36 | std::multimap > _map; 37 | 38 | void dump(logger& l, int level) const; 39 | 40 | static const char* skip(const char* str, bool all = true); 41 | 42 | bool parse_block(const char* *str); 43 | 44 | bool parse(const char* *str); 45 | 46 | public: 47 | conf(); 48 | 49 | static ptr load(const std::string& path); 50 | 51 | bool is_block() const; 52 | 53 | ptr operator[](const std::string& name) const; 54 | ptr operator()(const std::string& name, int index = 0) const; 55 | 56 | operator const std::string&() const; 57 | operator int() const; 58 | operator bool() const; 59 | 60 | bool as_bool() const; 61 | const std::string& as_str() const; 62 | int as_int() const; 63 | 64 | bool empty() const; 65 | 66 | std::vector > find_all(const std::string& name) const; 67 | 68 | ptr find(const std::string& name, int index = 0) const; 69 | 70 | void dump(int pri = LOG_INFO) const; 71 | 72 | operator const std::string&(); 73 | 74 | }; 75 | 76 | NDPPD_NS_END 77 | -------------------------------------------------------------------------------- /src/iface.cc: -------------------------------------------------------------------------------- 1 | // ndppd - NDP Proxy Daemon 2 | // Copyright (C) 2011 Daniel Adolfsson 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program. If not, see . 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | #include 36 | 37 | #include 38 | #include 39 | #include 40 | #include 41 | 42 | #include "ndppd.h" 43 | #include "route.h" 44 | 45 | NDPPD_NS_BEGIN 46 | 47 | std::map > iface::_map; 48 | 49 | bool iface::_map_dirty = false; 50 | 51 | std::vector iface::_pollfds; 52 | 53 | iface::iface() : 54 | _ifd(-1), _pfd(-1), _name("") 55 | { 56 | } 57 | 58 | iface::~iface() 59 | { 60 | logger::debug() << "iface::~iface()"; 61 | 62 | if (_ifd >= 0) 63 | close(_ifd); 64 | 65 | if (_pfd >= 0) { 66 | if (_prev_allmulti >= 0) { 67 | allmulti(_prev_allmulti); 68 | } 69 | if (_prev_promiscuous >= 0) { 70 | promiscuous(_prev_promiscuous); 71 | } 72 | close(_pfd); 73 | } 74 | 75 | _map_dirty = true; 76 | 77 | _serves.clear(); 78 | _parents.clear(); 79 | } 80 | 81 | ptr iface::open_pfd(const std::string& name, bool promiscuous) 82 | { 83 | int fd = 0; 84 | 85 | std::map >::iterator it = _map.find(name); 86 | 87 | ptr ifa; 88 | 89 | if (it != _map.end()) { 90 | if (it->second->_pfd >= 0) 91 | return it->second; 92 | 93 | ifa = it->second; 94 | } else { 95 | // We need an _ifs, so let's set one up. 96 | ifa = open_ifd(name); 97 | } 98 | 99 | if (!ifa) 100 | return ptr(); 101 | 102 | // Create a socket. 103 | 104 | if ((fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IPV6))) < 0) { 105 | logger::error() << "Unable to create socket"; 106 | return ptr(); 107 | } 108 | 109 | // Bind to the specified interface. 110 | 111 | struct sockaddr_ll lladdr; 112 | 113 | memset(&lladdr, 0, sizeof(struct sockaddr_ll)); 114 | lladdr.sll_family = AF_PACKET; 115 | lladdr.sll_protocol = htons(ETH_P_IPV6); 116 | 117 | if (!(lladdr.sll_ifindex = if_nametoindex(name.c_str()))) { 118 | close(fd); 119 | logger::error() << "Failed to bind to interface '" << name << "'"; 120 | return ptr(); 121 | } 122 | 123 | if (bind(fd, (struct sockaddr* )&lladdr, sizeof(struct sockaddr_ll)) < 0) { 124 | close(fd); 125 | logger::error() << "Failed to bind to interface '" << name << "'"; 126 | return ptr(); 127 | } 128 | 129 | // Switch to non-blocking mode. 130 | 131 | int on = 1; 132 | 133 | if (ioctl(fd, FIONBIO, (char* )&on) < 0) { 134 | close(fd); 135 | logger::error() << "Failed to switch to non-blocking on interface '" << name << "'"; 136 | return ptr(); 137 | } 138 | 139 | // Set up filter. 140 | 141 | static struct sock_filter filter[] = { 142 | // Load the ether_type. 143 | BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 144 | offsetof(struct ether_header, ether_type)), 145 | // Bail if it's* not* ETHERTYPE_IPV6. 146 | BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ETHERTYPE_IPV6, 0, 5), 147 | // Load the next header type. 148 | BPF_STMT(BPF_LD | BPF_B | BPF_ABS, 149 | sizeof(struct ether_header) + offsetof(struct ip6_hdr, ip6_nxt)), 150 | // Bail if it's* not* IPPROTO_ICMPV6. 151 | BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, IPPROTO_ICMPV6, 0, 3), 152 | // Load the ICMPv6 type. 153 | BPF_STMT(BPF_LD | BPF_B | BPF_ABS, 154 | sizeof(struct ether_header) + sizeof(ip6_hdr) + offsetof(struct icmp6_hdr, icmp6_type)), 155 | // Bail if it's* not* ND_NEIGHBOR_SOLICIT. 156 | BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ND_NEIGHBOR_SOLICIT, 0, 1), 157 | // Keep packet. 158 | BPF_STMT(BPF_RET | BPF_K, (u_int32_t)-1), 159 | // Drop packet. 160 | BPF_STMT(BPF_RET | BPF_K, 0) 161 | }; 162 | 163 | static struct sock_fprog fprog = { 164 | 8, 165 | filter 166 | }; 167 | 168 | if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog)) < 0) { 169 | logger::error() << "Failed to set filter"; 170 | return ptr(); 171 | } 172 | 173 | // Set up an instance of 'iface'. 174 | 175 | ifa->_pfd = fd; 176 | 177 | // Eh. Allmulti. 178 | ifa->_prev_allmulti = ifa->allmulti(1); 179 | 180 | // Eh. Promiscuous 181 | if (promiscuous == true) { 182 | ifa->_prev_promiscuous = ifa->promiscuous(1); 183 | } else { 184 | ifa->_prev_promiscuous = -1; 185 | } 186 | 187 | _map_dirty = true; 188 | 189 | return ifa; 190 | } 191 | 192 | ptr iface::open_ifd(const std::string& name) 193 | { 194 | int fd; 195 | 196 | std::map >::iterator it = _map.find(name); 197 | 198 | if ((it != _map.end()) && it->second->_ifd) 199 | return it->second; 200 | 201 | // Create a socket. 202 | 203 | if ((fd = socket(PF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0) { 204 | logger::error() << "Unable to create socket"; 205 | return ptr(); 206 | } 207 | 208 | // Bind to the specified interface. 209 | 210 | struct ifreq ifr; 211 | 212 | memset(&ifr, 0, sizeof(ifr)); 213 | strncpy(ifr.ifr_name, name.c_str(), IFNAMSIZ - 1); 214 | ifr.ifr_name[IFNAMSIZ - 1] = '\0'; 215 | 216 | if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE,& ifr, sizeof(ifr)) < 0) { 217 | close(fd); 218 | logger::error() << "Failed to bind to interface '" << name << "'"; 219 | return ptr(); 220 | } 221 | 222 | // Detect the link-layer address. 223 | 224 | memset(&ifr, 0, sizeof(ifr)); 225 | strncpy(ifr.ifr_name, name.c_str(), IFNAMSIZ - 1); 226 | ifr.ifr_name[IFNAMSIZ - 1] = '\0'; 227 | 228 | if (ioctl(fd, SIOCGIFHWADDR,& ifr) < 0) { 229 | close(fd); 230 | logger::error() 231 | << "Failed to detect link-layer address for interface '" 232 | << name << "'"; 233 | return ptr(); 234 | } 235 | 236 | logger::debug() 237 | << "fd=" << fd << ", hwaddr=" 238 | << ether_ntoa((const struct ether_addr* )&ifr.ifr_hwaddr.sa_data); 239 | 240 | // Set max hops. 241 | 242 | int hops = 255; 243 | 244 | if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &hops, 245 | sizeof(hops)) < 0) { 246 | close(fd); 247 | logger::error() << "iface::open_ifd() failed IPV6_MULTICAST_HOPS"; 248 | return ptr(); 249 | } 250 | 251 | if (setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &hops, 252 | sizeof(hops)) < 0) { 253 | close(fd); 254 | logger::error() << "iface::open_ifd() failed IPV6_UNICAST_HOPS"; 255 | return ptr(); 256 | } 257 | 258 | // Switch to non-blocking mode. 259 | 260 | int on = 1; 261 | 262 | if (ioctl(fd, FIONBIO, (char*)&on) < 0) { 263 | close(fd); 264 | logger::error() 265 | << "Failed to switch to non-blocking on interface '" 266 | << name << "'"; 267 | return ptr(); 268 | } 269 | 270 | // Set up filter. 271 | 272 | struct icmp6_filter filter; 273 | ICMP6_FILTER_SETBLOCKALL(&filter); 274 | ICMP6_FILTER_SETPASS(ND_NEIGHBOR_ADVERT, &filter); 275 | 276 | if (setsockopt(fd, IPPROTO_ICMPV6, ICMP6_FILTER,& filter, sizeof(filter)) < 0) { 277 | logger::error() << "Failed to set filter"; 278 | return ptr(); 279 | } 280 | 281 | // Set up an instance of 'iface'. 282 | 283 | ptr ifa; 284 | 285 | if (it == _map.end()) { 286 | ifa = new iface(); 287 | ifa->_name = name; 288 | ifa->_ptr = ifa; 289 | 290 | _map[name] = ifa; 291 | } else { 292 | ifa = it->second; 293 | } 294 | 295 | ifa->_ifd = fd; 296 | 297 | memcpy(&ifa->hwaddr, ifr.ifr_hwaddr.sa_data, sizeof(struct ether_addr)); 298 | 299 | _map_dirty = true; 300 | 301 | return ifa; 302 | } 303 | 304 | ssize_t iface::read(int fd, struct sockaddr* saddr, ssize_t saddr_size, uint8_t* msg, size_t size) 305 | { 306 | struct msghdr mhdr; 307 | struct iovec iov; 308 | int len; 309 | 310 | if (!msg || (size < 0)) 311 | return -1; 312 | 313 | iov.iov_len = size; 314 | iov.iov_base = (caddr_t)msg; 315 | 316 | memset(&mhdr, 0, sizeof(mhdr)); 317 | mhdr.msg_name = (caddr_t)saddr; 318 | mhdr.msg_namelen = saddr_size; 319 | mhdr.msg_iov =& iov; 320 | mhdr.msg_iovlen = 1; 321 | 322 | if ((len = recvmsg(fd,& mhdr, 0)) < 0) 323 | { 324 | logger::error() << "iface::read() failed! error=" << logger::err() << ", ifa=" << name(); 325 | return -1; 326 | } 327 | 328 | logger::debug() << "iface::read() ifa=" << name() << ", len=" << len; 329 | 330 | if (len < sizeof(struct icmp6_hdr)) 331 | return -1; 332 | 333 | return len; 334 | } 335 | 336 | ssize_t iface::write(int fd, const address& daddr, const uint8_t* msg, size_t size) 337 | { 338 | struct sockaddr_in6 daddr_tmp; 339 | struct msghdr mhdr; 340 | struct iovec iov; 341 | 342 | memset(&daddr_tmp, 0, sizeof(struct sockaddr_in6)); 343 | daddr_tmp.sin6_family = AF_INET6; 344 | daddr_tmp.sin6_port = htons(IPPROTO_ICMPV6); // Needed? 345 | memcpy(&daddr_tmp.sin6_addr,& daddr.const_addr(), sizeof(struct in6_addr)); 346 | 347 | iov.iov_len = size; 348 | iov.iov_base = (caddr_t)msg; 349 | 350 | memset(&mhdr, 0, sizeof(mhdr)); 351 | mhdr.msg_name = (caddr_t)&daddr_tmp; 352 | mhdr.msg_namelen = sizeof(sockaddr_in6); 353 | mhdr.msg_iov =& iov; 354 | mhdr.msg_iovlen = 1; 355 | 356 | logger::debug() << "iface::write() ifa=" << name() << ", daddr=" << daddr.to_string() << ", len=" 357 | << size; 358 | 359 | int len; 360 | 361 | if ((len = sendmsg(fd,& mhdr, 0)) < 0) 362 | { 363 | logger::error() << "iface::write() failed! error=" << logger::err() << ", ifa=" << name() << ", daddr=" << daddr.to_string(); 364 | return -1; 365 | } 366 | 367 | return len; 368 | } 369 | 370 | ssize_t iface::read_solicit(address& saddr, address& daddr, address& taddr) 371 | { 372 | struct sockaddr_ll t_saddr; 373 | uint8_t msg[256]; 374 | ssize_t len; 375 | 376 | if ((len = read(_pfd, (struct sockaddr*)&t_saddr, sizeof(struct sockaddr_ll), msg, sizeof(msg))) < 0) { 377 | logger::warning() << "iface::read_solicit() failed: " << logger::err(); 378 | return -1; 379 | } 380 | 381 | struct ip6_hdr* ip6h = 382 | (struct ip6_hdr* )(msg + ETH_HLEN); 383 | 384 | struct nd_neighbor_solicit* ns = 385 | (struct nd_neighbor_solicit*)(msg + ETH_HLEN + sizeof(struct ip6_hdr)); 386 | 387 | taddr = ns->nd_ns_target; 388 | daddr = ip6h->ip6_dst; 389 | saddr = ip6h->ip6_src; 390 | 391 | // Ignore packets sent from this machine 392 | if (iface::is_local(saddr) == true) { 393 | return 0; 394 | } 395 | 396 | logger::debug() << "iface::read_solicit() saddr=" << saddr.to_string() 397 | << ", daddr=" << daddr.to_string() << ", taddr=" << taddr.to_string() << ", len=" << len; 398 | 399 | return len; 400 | } 401 | 402 | ssize_t iface::write_solicit(const address& taddr) 403 | { 404 | char buf[128]; 405 | 406 | memset(buf, 0, sizeof(buf)); 407 | 408 | struct nd_neighbor_solicit* ns = 409 | (struct nd_neighbor_solicit* )&buf[0]; 410 | 411 | struct nd_opt_hdr* opt = 412 | (struct nd_opt_hdr* )&buf[sizeof(struct nd_neighbor_solicit)]; 413 | 414 | opt->nd_opt_type = ND_OPT_SOURCE_LINKADDR; 415 | opt->nd_opt_len = 1; 416 | 417 | ns->nd_ns_type = ND_NEIGHBOR_SOLICIT; 418 | 419 | memcpy(&ns->nd_ns_target,& taddr.const_addr(), sizeof(struct in6_addr)); 420 | 421 | memcpy(buf + sizeof(struct nd_neighbor_solicit) + sizeof(struct nd_opt_hdr), 422 | &hwaddr, 6); 423 | 424 | // FIXME: Alright, I'm lazy. 425 | static address multicast("ff02::1:ff00:0000"); 426 | 427 | address daddr; 428 | 429 | daddr = multicast; 430 | 431 | daddr.addr().s6_addr[13] = taddr.const_addr().s6_addr[13]; 432 | daddr.addr().s6_addr[14] = taddr.const_addr().s6_addr[14]; 433 | daddr.addr().s6_addr[15] = taddr.const_addr().s6_addr[15]; 434 | 435 | logger::debug() << "iface::write_solicit() taddr=" << taddr.to_string() 436 | << ", daddr=" << daddr.to_string(); 437 | 438 | return write(_ifd, daddr, (uint8_t* )buf, sizeof(struct nd_neighbor_solicit) 439 | + sizeof(struct nd_opt_hdr) + 6); 440 | } 441 | 442 | ssize_t iface::write_advert(const address& daddr, const address& taddr, bool router) 443 | { 444 | char buf[128]; 445 | 446 | memset(buf, 0, sizeof(buf)); 447 | 448 | struct nd_neighbor_advert* na = 449 | (struct nd_neighbor_advert* )&buf[0]; 450 | 451 | struct nd_opt_hdr* opt = 452 | (struct nd_opt_hdr* )&buf[sizeof(struct nd_neighbor_advert)]; 453 | 454 | opt->nd_opt_type = ND_OPT_TARGET_LINKADDR; 455 | opt->nd_opt_len = 1; 456 | 457 | na->nd_na_type = ND_NEIGHBOR_ADVERT; 458 | na->nd_na_flags_reserved = (daddr.is_multicast() ? 0 : ND_NA_FLAG_SOLICITED) | (router ? ND_NA_FLAG_ROUTER : 0); 459 | 460 | memcpy(&na->nd_na_target,& taddr.const_addr(), sizeof(struct in6_addr)); 461 | 462 | memcpy(buf + sizeof(struct nd_neighbor_advert) + sizeof(struct nd_opt_hdr), 463 | &hwaddr, 6); 464 | 465 | logger::debug() << "iface::write_advert() daddr=" << daddr.to_string() 466 | << ", taddr=" << taddr.to_string(); 467 | 468 | return write(_ifd, daddr, (uint8_t* )buf, sizeof(struct nd_neighbor_advert) + 469 | sizeof(struct nd_opt_hdr) + 6); 470 | } 471 | 472 | ssize_t iface::read_advert(address& saddr, address& taddr) 473 | { 474 | struct sockaddr_in6 t_saddr; 475 | uint8_t msg[256]; 476 | ssize_t len; 477 | 478 | memset(&t_saddr, 0, sizeof(struct sockaddr_in6)); 479 | t_saddr.sin6_family = AF_INET6; 480 | t_saddr.sin6_port = htons(IPPROTO_ICMPV6); // Needed? 481 | 482 | if ((len = read(_ifd, (struct sockaddr* )&t_saddr, sizeof(struct sockaddr_in6), msg, sizeof(msg))) < 0) { 483 | logger::warning() << "iface::read_advert() failed: " << logger::err(); 484 | return -1; 485 | } 486 | 487 | saddr = t_saddr.sin6_addr; 488 | 489 | // Ignore packets sent from this machine 490 | if (iface::is_local(saddr) == true) { 491 | return 0; 492 | } 493 | 494 | if (((struct icmp6_hdr* )msg)->icmp6_type != ND_NEIGHBOR_ADVERT) 495 | return -1; 496 | 497 | taddr = ((struct nd_neighbor_solicit* )msg)->nd_ns_target; 498 | 499 | logger::debug() << "iface::read_advert() saddr=" << saddr.to_string() << ", taddr=" << taddr.to_string() << ", len=" << len; 500 | 501 | return len; 502 | } 503 | 504 | bool iface::is_local(const address& addr) 505 | { 506 | // Check if the address is for an interface we own that is attached to 507 | // one of the slave interfaces 508 | for (std::list >::iterator ad = address::addresses_begin(); ad != address::addresses_end(); ad++) 509 | { 510 | if ((*ad)->addr() == addr) 511 | return true; 512 | } 513 | return false; 514 | } 515 | 516 | bool iface::handle_local(const address& saddr, const address& taddr) 517 | { 518 | // Check if the address is for an interface we own that is attached to 519 | // one of the slave interfaces 520 | for (std::list >::iterator ad = address::addresses_begin(); ad != address::addresses_end(); ad++) 521 | { 522 | if ((*ad)->addr() == taddr) 523 | { 524 | // Loop through all the serves that are using this iface to respond to NDP solicitation requests 525 | for (std::list >::iterator pit = serves_begin(); pit != serves_end(); pit++) { 526 | ptr pr = (*pit); 527 | if (!pr) continue; 528 | 529 | for (std::list >::iterator it = pr->rules_begin(); it != pr->rules_end(); it++) { 530 | ptr ru = *it; 531 | 532 | if (ru->daughter() && ru->daughter()->name() == (*ad)->ifname()) 533 | { 534 | logger::debug() << "proxy::handle_solicit() found local taddr=" << taddr; 535 | write_advert(saddr, taddr, false); 536 | return true; 537 | } 538 | } 539 | } 540 | } 541 | } 542 | 543 | return false; 544 | } 545 | 546 | void iface::handle_reverse_advert(const address& saddr, const std::string& ifname) 547 | { 548 | if (!saddr.is_unicast()) 549 | return; 550 | 551 | logger::debug() 552 | << "proxy::handle_reverse_advert()"; 553 | 554 | // Loop through all the parents that forward new NDP soliciation requests to this interface 555 | for (std::list >::iterator pit = parents_begin(); pit != parents_end(); pit++) { 556 | ptr parent = (*pit); 557 | if (!parent || !parent->ifa()) { 558 | continue; 559 | } 560 | 561 | // Setup the reverse path on any proxies that are dealing 562 | // with the reverse direction (this helps improve connectivity and 563 | // latency in a full duplex setup) 564 | for (std::list >::iterator it = parent->rules_begin(); it != parent->rules_end(); it++) { 565 | ptr ru = *it; 566 | 567 | if (ru->addr() == saddr && 568 | ru->daughter()->name() == ifname) 569 | { 570 | logger::debug() << " - generating artifical advertisement: " << ifname; 571 | parent->handle_stateless_advert(saddr, saddr, ifname, ru->autovia()); 572 | } 573 | } 574 | } 575 | } 576 | 577 | void iface::fixup_pollfds() 578 | { 579 | _pollfds.resize(_map.size()* 2); 580 | 581 | int i = 0; 582 | 583 | logger::debug() << "iface::fixup_pollfds() _map.size()=" << _map.size(); 584 | 585 | for (std::map >::iterator it = _map.begin(); 586 | it != _map.end(); it++) { 587 | _pollfds[i].fd = it->second->_ifd; 588 | _pollfds[i].events = POLLIN; 589 | _pollfds[i].revents = 0; 590 | i++; 591 | 592 | _pollfds[i].fd = it->second->_pfd; 593 | _pollfds[i].events = POLLIN; 594 | _pollfds[i].revents = 0; 595 | i++; 596 | } 597 | } 598 | 599 | void iface::cleanup() 600 | { 601 | for (std::map >::iterator it = _map.begin(); 602 | it != _map.end(); ) { 603 | std::map >::iterator c_it = it++; 604 | if (!c_it->second) { 605 | _map.erase(c_it); 606 | } 607 | } 608 | } 609 | 610 | int iface::poll_all() 611 | { 612 | if (_map_dirty) { 613 | cleanup(); 614 | fixup_pollfds(); 615 | _map_dirty = false; 616 | } 617 | 618 | if (_pollfds.size() == 0) { 619 | ::sleep(1); 620 | return 0; 621 | } 622 | 623 | assert(_pollfds.size() == _map.size()* 2); 624 | 625 | int len; 626 | 627 | if ((len = ::poll(&_pollfds[0], _pollfds.size(), 50)) < 0) { 628 | logger::error() << "Failed to poll interfaces: " << logger::err(); 629 | return -1; 630 | } 631 | 632 | if (len == 0) { 633 | return 0; 634 | } 635 | 636 | std::map >::iterator i_it = _map.begin(); 637 | 638 | int i = 0; 639 | 640 | for (std::vector::iterator f_it = _pollfds.begin(); 641 | f_it != _pollfds.end(); f_it++) { 642 | assert(i_it != _map.end()); 643 | 644 | if (i && !(i % 2)) { 645 | i_it++; 646 | } 647 | 648 | bool is_pfd = i++ % 2; 649 | 650 | ptr ifa = i_it->second; 651 | 652 | if (f_it->revents & POLLERR) { 653 | logger::error() << "Error polling interface " << ifa->_name.c_str(); 654 | return -1; 655 | } 656 | 657 | if (!(f_it->revents & POLLIN)) { 658 | continue; 659 | } 660 | 661 | address saddr, daddr, taddr; 662 | ssize_t size; 663 | 664 | if (is_pfd) { 665 | size = ifa->read_solicit(saddr, daddr, taddr); 666 | if (size < 0) { 667 | logger::error() << "Failed to read from interface '%s'", ifa->_name.c_str(); 668 | continue; 669 | } 670 | if (size == 0) { 671 | logger::debug() << "iface::read_solicit() loopback received and ignored"; 672 | continue; 673 | } 674 | 675 | // Process any local addresses for interfaces that we are proxying 676 | if (ifa->handle_local(saddr, taddr) == true) { 677 | continue; 678 | } 679 | 680 | // We have to handle all the parents who may be interested in 681 | // the reverse path towards the one who sent this solicit. 682 | // In fact, the parent need to know the source address in order 683 | // to respond to NDP Solicitations 684 | ifa->handle_reverse_advert(saddr, ifa->name()); 685 | 686 | // Loop through all the proxies that are using this iface to respond to NDP solicitation requests 687 | bool handled = false; 688 | for (std::list >::iterator pit = ifa->serves_begin(); pit != ifa->serves_end(); pit++) { 689 | ptr pr = (*pit); 690 | if (!pr) continue; 691 | 692 | // Process the solicitation request by relating it to other 693 | // interfaces or lookup up any statics routes we have configured 694 | handled = true; 695 | pr->handle_solicit(saddr, taddr, ifa->name()); 696 | } 697 | 698 | // If it was not handled then write an error message 699 | if (handled == false) { 700 | logger::debug() << " - solicit was ignored"; 701 | } 702 | 703 | } else { 704 | size = ifa->read_advert(saddr, taddr); 705 | if (size < 0) { 706 | logger::error() << "Failed to read from interface '%s'", ifa->_name.c_str(); 707 | continue; 708 | } 709 | if (size == 0) { 710 | logger::debug() << "iface::read_advert() loopback received and ignored"; 711 | continue; 712 | } 713 | 714 | // Process the NDP advert 715 | bool handled = false; 716 | for (std::list >::iterator pit = ifa->parents_begin(); pit != ifa->parents_end(); pit++) { 717 | ptr pr = (*pit); 718 | if (!pr || !pr->ifa()) { 719 | continue; 720 | } 721 | 722 | // The proxy must have a rule for this interface or it is not meant to receive 723 | // any notifications and thus they must be ignored 724 | bool autovia = false; 725 | bool is_relevant = false; 726 | for (std::list >::iterator it = pr->rules_begin(); it != pr->rules_end(); it++) { 727 | ptr ru = *it; 728 | 729 | if (ru->addr() == taddr && 730 | ru->daughter() && 731 | ru->daughter()->name() == ifa->name()) 732 | { 733 | is_relevant = true; 734 | autovia = ru->autovia(); 735 | break; 736 | } 737 | } 738 | if (is_relevant == false) { 739 | logger::debug() << "iface::read_advert() advert is not for " << ifa->name() << "...skipping"; 740 | continue; 741 | } 742 | 743 | // Process the NDP advertisement 744 | handled = true; 745 | pr->handle_advert(saddr, taddr, ifa->name(), autovia); 746 | } 747 | 748 | // If it was not handled then write an error message 749 | if (handled == false) { 750 | logger::debug() << " - advert was ignored"; 751 | } 752 | } 753 | } 754 | 755 | return 0; 756 | } 757 | 758 | int iface::allmulti(int state) 759 | { 760 | struct ifreq ifr; 761 | 762 | logger::debug() 763 | << "iface::allmulti() state=" 764 | << state << ", _name=\"" << _name << "\""; 765 | 766 | state = !!state; 767 | 768 | memset(&ifr, 0, sizeof(ifr)); 769 | 770 | strncpy(ifr.ifr_name, _name.c_str(), IFNAMSIZ); 771 | 772 | if (ioctl(_pfd, SIOCGIFFLAGS, &ifr) < 0) { 773 | logger::error() << "Failed to get allmulti: " << logger::err(); 774 | return -1; 775 | } 776 | 777 | int old_state = !!(ifr.ifr_flags &IFF_ALLMULTI); 778 | 779 | if (state == old_state) { 780 | return old_state; 781 | } 782 | 783 | if (state) { 784 | ifr.ifr_flags |= IFF_ALLMULTI; 785 | } else { 786 | ifr.ifr_flags &= ~IFF_ALLMULTI; 787 | } 788 | 789 | if (ioctl(_pfd, SIOCSIFFLAGS, &ifr) < 0) { 790 | logger::error() << "Failed to set allmulti: " << logger::err(); 791 | return -1; 792 | } 793 | 794 | return old_state; 795 | } 796 | 797 | int iface::promiscuous(int state) 798 | { 799 | struct ifreq ifr; 800 | 801 | logger::debug() 802 | << "iface::promiscuous() state=" 803 | << state << ", _name=\"" << _name << "\""; 804 | 805 | state = !!state; 806 | 807 | memset(&ifr, 0, sizeof(ifr)); 808 | 809 | strncpy(ifr.ifr_name, _name.c_str(), IFNAMSIZ); 810 | 811 | if (ioctl(_pfd, SIOCGIFFLAGS, &ifr) < 0) { 812 | logger::error() << "Failed to get promiscuous: " << logger::err(); 813 | return -1; 814 | } 815 | 816 | int old_state = !!(ifr.ifr_flags &IFF_PROMISC); 817 | 818 | if (state == old_state) { 819 | return old_state; 820 | } 821 | 822 | if (state) { 823 | ifr.ifr_flags |= IFF_PROMISC; 824 | } else { 825 | ifr.ifr_flags &= ~IFF_PROMISC; 826 | } 827 | 828 | if (ioctl(_pfd, SIOCSIFFLAGS, &ifr) < 0) { 829 | logger::error() << "Failed to set promiscuous: " << logger::err(); 830 | return -1; 831 | } 832 | 833 | return old_state; 834 | } 835 | 836 | const std::string& iface::name() const 837 | { 838 | return _name; 839 | } 840 | 841 | void iface::add_serves(const ptr& pr) 842 | { 843 | _serves.push_back(pr); 844 | } 845 | 846 | std::list >::iterator iface::serves_begin() 847 | { 848 | return _serves.begin(); 849 | } 850 | 851 | std::list >::iterator iface::serves_end() 852 | { 853 | return _serves.end(); 854 | } 855 | 856 | void iface::add_parent(const ptr& pr) 857 | { 858 | _parents.push_back(pr); 859 | } 860 | 861 | std::list >::iterator iface::parents_begin() 862 | { 863 | return _parents.begin(); 864 | } 865 | 866 | std::list >::iterator iface::parents_end() 867 | { 868 | return _parents.end(); 869 | } 870 | 871 | NDPPD_NS_END 872 | -------------------------------------------------------------------------------- /src/iface.h: -------------------------------------------------------------------------------- 1 | // ndppd - NDP Proxy Daemon 2 | // Copyright (C) 2011 Daniel Adolfsson 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program. If not, see . 16 | #pragma once 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include 24 | #include 25 | 26 | #include "ndppd.h" 27 | 28 | NDPPD_NS_BEGIN 29 | 30 | class session; 31 | class proxy; 32 | 33 | class iface { 34 | public: 35 | 36 | // Destructor. 37 | ~iface(); 38 | 39 | static ptr open_ifd(const std::string& name); 40 | 41 | static ptr open_pfd(const std::string& name, bool promiscuous); 42 | 43 | static int poll_all(); 44 | 45 | ssize_t read(int fd, struct sockaddr* saddr, ssize_t saddr_size, uint8_t* msg, size_t size); 46 | 47 | ssize_t write(int fd, const address& daddr, const uint8_t* msg, size_t size); 48 | 49 | // Writes a NB_NEIGHBOR_SOLICIT message to the _ifd socket. 50 | ssize_t write_solicit(const address& taddr); 51 | 52 | // Writes a NB_NEIGHBOR_ADVERT message to the _ifd socket; 53 | ssize_t write_advert(const address& daddr, const address& taddr, bool router); 54 | 55 | // Reads a NB_NEIGHBOR_SOLICIT message from the _pfd socket. 56 | ssize_t read_solicit(address& saddr, address& daddr, address& taddr); 57 | 58 | // Reads a NB_NEIGHBOR_ADVERT message from the _ifd socket; 59 | ssize_t read_advert(address& saddr, address& taddr); 60 | 61 | bool handle_local(const address& saddr, const address& taddr); 62 | 63 | bool is_local(const address& addr); 64 | 65 | void handle_reverse_advert(const address& saddr, const std::string& ifname); 66 | 67 | // Returns the name of the interface. 68 | const std::string& name() const; 69 | 70 | std::list >::iterator serves_begin(); 71 | 72 | std::list >::iterator serves_end(); 73 | 74 | void add_serves(const ptr& proxy); 75 | 76 | std::list >::iterator parents_begin(); 77 | 78 | std::list >::iterator parents_end(); 79 | 80 | void add_parent(const ptr& parent); 81 | 82 | static std::map > _map; 83 | 84 | private: 85 | 86 | static bool _map_dirty; 87 | 88 | // An array of objects used with ::poll. 89 | static std::vector _pollfds; 90 | 91 | // Updates the array above. 92 | static void fixup_pollfds(); 93 | 94 | static void cleanup(); 95 | 96 | // Weak pointer so this object can reference itself. 97 | weak_ptr _ptr; 98 | 99 | // The "generic" ICMPv6 socket for reading/writing NB_NEIGHBOR_ADVERT 100 | // messages as well as writing NB_NEIGHBOR_SOLICIT messages. 101 | int _ifd; 102 | 103 | // This is the PF_PACKET socket we use in order to read 104 | // NB_NEIGHBOR_SOLICIT messages. 105 | int _pfd; 106 | 107 | // Previous state of ALLMULTI for the interface. 108 | int _prev_allmulti; 109 | 110 | // Previous state of PROMISC for the interface 111 | int _prev_promiscuous; 112 | 113 | // Name of this interface. 114 | std::string _name; 115 | 116 | std::list > _serves; 117 | 118 | std::list > _parents; 119 | 120 | // The link-layer address of this interface. 121 | struct ether_addr hwaddr; 122 | 123 | // Turns on/off ALLMULTI for this interface - returns the previous state 124 | // or -1 if there was an error. 125 | int allmulti(int state); 126 | 127 | // Turns on/off PROMISC for this interface - returns the previous state 128 | // or -1 if there was an error 129 | int promiscuous(int state); 130 | 131 | // Constructor. 132 | iface(); 133 | }; 134 | 135 | NDPPD_NS_END 136 | -------------------------------------------------------------------------------- /src/logger.cc: -------------------------------------------------------------------------------- 1 | // ndppd - NDP Proxy Daemon 2 | // Copyright (C) 2011 Daniel Adolfsson 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program. If not, see . 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include "ndppd.h" 26 | #include "logger.h" 27 | 28 | NDPPD_NS_BEGIN 29 | 30 | /*const char* log::_level_str[] = 31 | { 32 | "fatal", 33 | "alert", 34 | "critical", 35 | "error", 36 | "warning", 37 | "notice", 38 | "info", 39 | "debug" 40 | };*/ 41 | 42 | int logger::_max_pri = LOG_NOTICE; 43 | 44 | bool logger::_syslog = false; 45 | 46 | const logger::pri_name logger::_pri_names[] = { 47 | { "emergency", LOG_EMERG }, 48 | { "alert", LOG_ALERT }, 49 | { "critical", LOG_CRIT }, 50 | { "error", LOG_ERR }, 51 | { "warning", LOG_WARNING }, 52 | { "notice", LOG_NOTICE }, 53 | { "info", LOG_INFO }, 54 | { "debug", LOG_DEBUG }, 55 | { NULL, 0 } 56 | }; 57 | 58 | logger::logger(int pri) : 59 | _pri(pri), _force_log(false) 60 | { 61 | } 62 | 63 | logger::logger(const logger& l) : 64 | _pri(l._pri), _ss(l._ss.str()), _force_log(false) 65 | { 66 | } 67 | 68 | logger::~logger() 69 | { 70 | flush(); 71 | } 72 | 73 | std::string logger::format(const std::string& fmt, ...) 74 | { 75 | char buf[2048]; 76 | va_list va; 77 | va_start(va, fmt); 78 | vsnprintf(buf, sizeof(buf), fmt.c_str(), va); 79 | va_end(va); 80 | return buf; 81 | } 82 | 83 | std::string logger::err() 84 | { 85 | char buf[2048]; 86 | 87 | #if (_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600) && ! _GNU_SOURCE 88 | if (strerror_r(errno, buf, sizeof(buf)) 89 | return "Unknown error"; 90 | return buf; 91 | #else 92 | return strerror_r(errno, buf, sizeof(buf)); 93 | #endif 94 | } 95 | 96 | logger logger::error() 97 | { 98 | return logger(LOG_ERR); 99 | } 100 | 101 | logger logger::info() 102 | { 103 | return logger(LOG_INFO); 104 | } 105 | 106 | logger logger::warning() 107 | { 108 | return logger(LOG_WARNING); 109 | } 110 | 111 | logger logger::debug() 112 | { 113 | return logger(LOG_DEBUG); 114 | } 115 | 116 | logger logger::notice() 117 | { 118 | return logger(LOG_NOTICE); 119 | } 120 | 121 | logger& logger::operator<<(const std::string& str) 122 | { 123 | _ss << str; 124 | return *this; 125 | } 126 | 127 | logger& logger::operator<<(int n) 128 | { 129 | _ss << n; 130 | return *this; 131 | } 132 | 133 | logger& logger::operator<<(logger& (*pf)(logger& )) 134 | { 135 | pf(*this); 136 | return *this; 137 | } 138 | 139 | logger& logger::endl(logger& __l) 140 | { 141 | __l.flush(); 142 | return __l; 143 | } 144 | 145 | logger& logger::force_log(bool b) 146 | { 147 | _force_log = b; 148 | return *this; 149 | } 150 | 151 | void logger::flush() 152 | { 153 | if (!_ss.rdbuf()->in_avail()) 154 | return; 155 | 156 | if (!_force_log && (_pri > _max_pri)) 157 | return; 158 | 159 | #ifndef DISABLE_SYSLOG 160 | if (_syslog) { 161 | ::syslog(_pri, "(%s) %s", _pri_names[_pri].name, _ss.str().c_str()); 162 | return; 163 | } 164 | #endif 165 | 166 | std::cout << "(" << _pri_names[_pri].name << ") " << _ss.str() << std::endl; 167 | 168 | _ss.str(""); 169 | } 170 | 171 | #ifndef DISABLE_SYSLOG 172 | void logger::syslog(bool sl) 173 | { 174 | if (sl == _syslog) 175 | return; 176 | 177 | if (_syslog = sl) { 178 | setlogmask(LOG_UPTO(LOG_DEBUG)); 179 | openlog("ndppd", LOG_CONS | LOG_NDELAY | LOG_PERROR | LOG_PID, LOG_USER); 180 | } else { 181 | closelog(); 182 | } 183 | } 184 | 185 | bool logger::syslog() 186 | { 187 | return _syslog; 188 | } 189 | #endif 190 | 191 | void logger::max_pri(int pri) 192 | { 193 | _max_pri = pri; 194 | } 195 | 196 | int logger::verbosity() 197 | { 198 | return _max_pri; 199 | } 200 | 201 | void logger::verbosity(int pri) 202 | { 203 | if ((pri >= 0) && (pri <= 7)) { 204 | _max_pri = pri; 205 | } 206 | } 207 | 208 | bool logger::verbosity(const std::string& name) 209 | { 210 | const char* c_name = name.c_str(); 211 | 212 | if (!*c_name) { 213 | return false; 214 | } 215 | 216 | if (isdigit(*c_name)) { 217 | _max_pri = atoi(c_name); 218 | return true; 219 | } 220 | 221 | for (int i = 0; _pri_names[i].name; i++) { 222 | if (!strncmp(c_name, _pri_names[i].name, strlen(_pri_names[i].name))) { 223 | _max_pri = _pri_names[i].pri; 224 | return true; 225 | } 226 | } 227 | 228 | return false; 229 | } 230 | 231 | NDPPD_NS_END 232 | -------------------------------------------------------------------------------- /src/logger.h: -------------------------------------------------------------------------------- 1 | // ndppd - NDP Proxy Daemon 2 | // Copyright (C) 2011 Daniel Adolfsson 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program. If not, see . 16 | #pragma once 17 | 18 | #include 19 | 20 | #ifndef DISABLE_SYSLOG 21 | # include 22 | #else 23 | # define LOG_EMERG 0 /* system is unusable */ 24 | # define LOG_ALERT 1 /* action must be taken immediately */ 25 | # define LOG_CRIT 2 /* critical conditions */ 26 | # define LOG_ERR 3 /* error conditions */ 27 | # define LOG_WARNING 4 /* warning conditions */ 28 | # define LOG_NOTICE 5 /* normal but significant condition */ 29 | # define LOG_INFO 6 /* informational */ 30 | # define LOG_DEBUG 7 /* debug-level messages */ 31 | #endif 32 | 33 | NDPPD_NS_BEGIN 34 | 35 | class logger { 36 | public: 37 | logger(int pri = LOG_NOTICE); 38 | 39 | logger(const logger& l); 40 | 41 | ~logger(); 42 | 43 | static std::string format(const std::string& fmt, ...); 44 | 45 | static void syslog(bool enable); 46 | static bool syslog(); 47 | 48 | static void max_pri(int pri); 49 | 50 | void flush(); 51 | 52 | static bool verbosity(const std::string& name); 53 | 54 | static int verbosity(); 55 | 56 | static void verbosity(int pri); 57 | 58 | logger& operator<<(const std::string& str); 59 | logger& operator<<(logger& (*pf)(logger& )); 60 | logger& operator<<(int n); 61 | 62 | logger& force_log(bool b = true); 63 | 64 | static logger& endl(logger& __l); 65 | 66 | // Shortcuts. 67 | 68 | static logger error(); 69 | static logger info(); 70 | static logger warning(); 71 | static logger debug(); 72 | static logger notice(); 73 | 74 | static std::string err(); 75 | 76 | private: 77 | int _pri; 78 | 79 | std::stringstream _ss; 80 | 81 | bool _force_log; 82 | 83 | struct pri_name { 84 | const char* name; 85 | int pri; 86 | }; 87 | 88 | static const pri_name _pri_names[]; 89 | 90 | static bool _syslog; 91 | 92 | static int _max_pri; 93 | 94 | 95 | }; 96 | 97 | NDPPD_NS_END 98 | -------------------------------------------------------------------------------- /src/nd-netlink.cc: -------------------------------------------------------------------------------- 1 | // 2 | //@file nd-netlink.cc 3 | // 4 | // Copyright 2016, Allied Telesis Labs New Zealand, Ltd 5 | // 6 | // This program is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation, either version 3 of the License, or 9 | // (at your option) any later version. 10 | // 11 | // This program is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | // 16 | // You should have received a copy of the GNU General Public License 17 | // along with this program. If not, see . 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include "ndppd.h" 24 | #include 25 | 26 | NDPPD_NS_BEGIN 27 | 28 | pthread_mutex_t cs_mutex = PTHREAD_MUTEX_INITIALIZER; 29 | 30 | struct in6_addr* 31 | address_create_ipv6(struct in6_addr *local) 32 | { 33 | struct in6_addr *addr = (struct in6_addr *)calloc(1, sizeof(struct in6_addr)); 34 | memcpy(addr, local, sizeof(struct in6_addr)); 35 | return addr; 36 | } 37 | 38 | void if_add_to_list(int ifindex, const ptr& ifa) 39 | { 40 | bool found = false; 41 | 42 | pthread_mutex_lock (&cs_mutex); 43 | for (std::vector::iterator it = interfaces.begin(); 44 | it != interfaces.end(); it++) { 45 | if ((*it).ifindex == ifindex) { 46 | found = true; 47 | break; 48 | } 49 | } 50 | if (!found) { 51 | logger::debug() << "rule::add_iface() if=" << ifa->name(); 52 | interface anInterface; 53 | anInterface._name = ifa->name(); 54 | anInterface.ifindex = ifindex; 55 | interfaces.push_back(anInterface); 56 | } 57 | pthread_mutex_unlock (&cs_mutex); 58 | } 59 | 60 | void 61 | if_addr_add(int ifindex, struct in6_addr *iaddr) 62 | { 63 | pthread_mutex_lock (&cs_mutex); 64 | for (std::vector::iterator it = interfaces.begin(); 65 | it != interfaces.end(); it++) { 66 | if ((*it).ifindex == ifindex) { 67 | address addr = address(*iaddr); 68 | logger::debug() << "Adding addr " << addr.to_string(); 69 | std::list
::iterator it_addr; 70 | it_addr = std::find((*it).addresses.begin(), (*it).addresses.end(), addr); 71 | if (it_addr == (*it).addresses.end()) { 72 | (*it).addresses.push_back(addr); 73 | } 74 | break; 75 | } 76 | } 77 | free(iaddr); 78 | pthread_mutex_unlock (&cs_mutex); 79 | } 80 | 81 | void 82 | if_addr_del(int ifindex, struct in6_addr *iaddr) 83 | { 84 | pthread_mutex_lock (&cs_mutex); 85 | for (std::vector::iterator it = interfaces.begin(); 86 | it != interfaces.end(); it++) { 87 | if ((*it).ifindex == ifindex) { 88 | address addr = address(*iaddr); 89 | logger::debug() << "Deleting addr " << addr.to_string(); 90 | (*it).addresses.remove(addr); 91 | break; 92 | } 93 | } 94 | free(iaddr); 95 | pthread_mutex_unlock (&cs_mutex); 96 | } 97 | 98 | bool 99 | if_addr_find(std::string iface, const struct in6_addr *iaddr) 100 | { 101 | bool found = false; 102 | 103 | pthread_mutex_lock (&cs_mutex); 104 | for (std::vector::iterator it = interfaces.begin(); 105 | it != interfaces.end(); it++) { 106 | if (iface.compare((*it)._name) == 0) { 107 | address addr = address(*iaddr); 108 | std::list
::iterator it_addr; 109 | it_addr = std::find((*it).addresses.begin(), (*it).addresses.end(), addr); 110 | if (it_addr != (*it).addresses.end()) { 111 | found = true; 112 | break; 113 | } 114 | } 115 | } 116 | pthread_mutex_unlock (&cs_mutex); 117 | return found; 118 | } 119 | 120 | static void 121 | nl_msg_newaddr(struct nlmsghdr *hdr) 122 | { 123 | struct ifaddrmsg *ifaddr = 124 | (struct ifaddrmsg *)(((char *) hdr) + (sizeof(struct nlmsghdr))); 125 | // parse the attributes 126 | struct nlattr *attrs[IFA_MAX + 1]; 127 | struct nlattr *s = (struct nlattr *)(((char *) ifaddr) + (sizeof(struct ifaddrmsg))); 128 | int len = nlmsg_datalen(hdr) - sizeof(struct ifinfomsg); 129 | memset(&attrs, '\0', sizeof(attrs)); 130 | nla_parse(attrs, IFA_MAX, s, len, NULL); 131 | 132 | struct in6_addr* addr = NULL; 133 | 134 | if (ifaddr->ifa_family == AF_INET6) { 135 | addr = address_create_ipv6((struct in6_addr *)nla_data(attrs[IFA_ADDRESS])); 136 | if_addr_add(ifaddr->ifa_index, addr); 137 | } 138 | } 139 | 140 | static void 141 | nl_msg_deladdr(struct nlmsghdr *hdr) 142 | { 143 | struct ifaddrmsg *ifaddr = 144 | (struct ifaddrmsg *)(((char *) hdr) + (sizeof(struct nlmsghdr))); 145 | // parse the attributes 146 | struct nlattr *attrs[IFA_MAX + 1]; 147 | struct nlattr *s = (struct nlattr *)(((char *) ifaddr) + (sizeof(struct ifaddrmsg))); 148 | int len = nlmsg_datalen(hdr) - sizeof(struct ifinfomsg); 149 | memset(&attrs, '\0', sizeof(attrs)); 150 | nla_parse(attrs, IFA_MAX, s, len, NULL); 151 | 152 | struct in6_addr* addr = NULL; 153 | 154 | if (ifaddr->ifa_family == AF_INET6) { 155 | addr = address_create_ipv6((struct in6_addr *)nla_data(attrs[IFA_ADDRESS])); 156 | if_addr_del(ifaddr->ifa_index, addr); 157 | } 158 | } 159 | 160 | static void 161 | new_addr(struct nl_object *obj, void *p) 162 | { 163 | struct rtnl_addr *addr = (struct rtnl_addr *) obj; 164 | struct nl_addr *local = rtnl_addr_get_local(addr); 165 | int family = rtnl_addr_get_family(addr); 166 | int ifindex = rtnl_addr_get_ifindex(addr); 167 | struct in6_addr* in_addr = NULL; 168 | 169 | char ipstr[INET6_ADDRSTRLEN]; 170 | inet_ntop(family, nl_addr_get_binary_addr(local), ipstr, INET6_ADDRSTRLEN); 171 | 172 | switch (family) { 173 | case AF_INET: 174 | break; 175 | case AF_INET6: 176 | in_addr = address_create_ipv6((struct in6_addr *)nl_addr_get_binary_addr(local)); 177 | if_addr_add(ifindex, in_addr); 178 | break; 179 | default: 180 | logger::error() << "Unknown message family: " << family; 181 | } 182 | } 183 | 184 | static int 185 | nl_msg_handler(struct nl_msg *msg, void *arg) 186 | { 187 | logger::debug() << "nl_msg_handler"; 188 | struct nlmsghdr *hdr = nlmsg_hdr(msg); 189 | 190 | switch (hdr->nlmsg_type) { 191 | case RTM_NEWADDR: 192 | nl_msg_newaddr(hdr); 193 | break; 194 | case RTM_DELADDR: 195 | nl_msg_deladdr(hdr); 196 | break; 197 | default: 198 | logger::error() << "Unknown message type: " << hdr->nlmsg_type; 199 | } 200 | 201 | return NL_OK; 202 | } 203 | 204 | static void * 205 | netlink_monitor(void *p) 206 | { 207 | struct nl_sock *sock = (struct nl_sock *) p; 208 | struct nl_cache *addr_cache; 209 | 210 | // get all the current addresses 211 | if (rtnl_addr_alloc_cache(sock, &addr_cache) < 0) { 212 | perror("rtnl_addr_alloc_cache"); 213 | return NULL; 214 | } 215 | 216 | // add existing addresses 217 | nl_cache_foreach(addr_cache, new_addr, NULL); 218 | // destroy the cache 219 | nl_cache_free(addr_cache); 220 | 221 | // switch to notification mode 222 | // disable sequence checking 223 | nl_socket_disable_seq_check(sock); 224 | // set the callback we want 225 | nl_socket_modify_cb(sock, NL_CB_VALID, NL_CB_CUSTOM, nl_msg_handler, NULL); 226 | 227 | // subscribe to the IPv6 address change callbacks 228 | nl_socket_add_memberships(sock, RTNLGRP_IPV6_IFADDR, 0); 229 | 230 | while (1) 231 | { 232 | nl_recvmsgs_default(sock); 233 | } 234 | return NULL; 235 | } 236 | 237 | static pthread_t monitor_thread; 238 | static struct nl_sock *monitor_sock; 239 | struct nl_sock *control_sock; 240 | 241 | bool 242 | netlink_setup() 243 | { 244 | // create a netlink socket 245 | control_sock = nl_socket_alloc(); 246 | nl_connect(control_sock, NETLINK_ROUTE); 247 | 248 | // create a thread to run the netlink monitor in 249 | // create a netlink socket 250 | monitor_sock = nl_socket_alloc(); 251 | nl_connect(monitor_sock, NETLINK_ROUTE); 252 | // increase the recv buffer size to capture all notifications 253 | nl_socket_set_buffer_size(monitor_sock, 2048000, 0); 254 | 255 | pthread_create(&monitor_thread, NULL, netlink_monitor, monitor_sock); 256 | pthread_setname_np(monitor_thread, "netlink"); 257 | if (pthread_setschedprio(monitor_thread, -10) < 0) 258 | { 259 | logger::warning() << "setschedprio: " << strerror(errno); 260 | } 261 | return true; 262 | } 263 | 264 | bool 265 | netlink_teardown() 266 | { 267 | void *res = 0; 268 | pthread_cancel(monitor_thread); 269 | pthread_join(monitor_thread, &res); 270 | nl_socket_free(monitor_sock); 271 | nl_socket_free(control_sock); 272 | return true; 273 | } 274 | 275 | NDPPD_NS_END 276 | -------------------------------------------------------------------------------- /src/nd-netlink.h: -------------------------------------------------------------------------------- 1 | // 2 | // @file nd-netlink.h 3 | // 4 | // Copyright 2016, Allied Telesis Labs New Zealand, Ltd 5 | // 6 | // This program is free software: you can redistribute it and/or modify 7 | // it under the terms of the GNU General Public License as published by 8 | // the Free Software Foundation, either version 3 of the License, or 9 | // (at your option) any later version. 10 | // 11 | // This program is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | // 16 | // You should have received a copy of the GNU General Public License 17 | // along with this program. If not, see . 18 | 19 | #pragma once 20 | 21 | NDPPD_NS_BEGIN 22 | 23 | bool netlink_teardown(); 24 | bool netlink_setup(); 25 | bool if_addr_find(std::string iface, const struct in6_addr *iaddr); 26 | void if_add_to_list(int ifindex, const ptr& ifa); 27 | 28 | NDPPD_NS_END 29 | -------------------------------------------------------------------------------- /src/ndppd.cc: -------------------------------------------------------------------------------- 1 | // ndppd - NDP Proxy Daemon 2 | // Copyright (C) 2011 Daniel Adolfsson 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program. If not, see . 16 | #include 17 | #include 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include 25 | #include 26 | 27 | #include 28 | #include 29 | #include 30 | 31 | #include "ndppd.h" 32 | #include "route.h" 33 | 34 | using namespace ndppd; 35 | 36 | static int daemonize() 37 | { 38 | pid_t pid = fork(); 39 | if (pid < 0) { 40 | logger::error() << "Failed to fork during daemonize: " << logger::err(); 41 | return -1; 42 | } 43 | 44 | if (pid > 0) 45 | exit(0); 46 | 47 | umask(0); 48 | 49 | pid_t sid = setsid(); 50 | if (sid < 0) { 51 | logger::error() << "Failed to setsid during daemonize: " << logger::err(); 52 | return -1; 53 | } 54 | 55 | if (chdir("/") < 0) { 56 | logger::error() << "Failed to change path during daemonize: " << logger::err(); 57 | return -1; 58 | } 59 | 60 | close(STDIN_FILENO); 61 | close(STDOUT_FILENO); 62 | close(STDERR_FILENO); 63 | 64 | return 0; 65 | } 66 | 67 | static ptr load_config(const std::string& path) 68 | { 69 | ptr cf, x_cf; 70 | 71 | if (!(cf = conf::load(path))) 72 | return (conf*)NULL; 73 | 74 | std::vector >::const_iterator p_it; 75 | 76 | std::vector > proxies(cf->find_all("proxy")); 77 | 78 | for (p_it = proxies.begin(); p_it != proxies.end(); p_it++) { 79 | ptr pr_cf = *p_it; 80 | 81 | if (pr_cf->empty()) { 82 | logger::error() << "'proxy' section is missing interface name"; 83 | return (conf*)NULL; 84 | } 85 | 86 | std::vector >::const_iterator r_it; 87 | 88 | std::vector > rules(pr_cf->find_all("rule")); 89 | 90 | for (r_it = rules.begin(); r_it != rules.end(); r_it++) { 91 | ptr ru_cf =* r_it; 92 | 93 | if (ru_cf->empty()) { 94 | logger::error() << "'rule' is missing an IPv6 address/net"; 95 | return (conf*)NULL; 96 | } 97 | 98 | address addr(*ru_cf); 99 | 100 | if (x_cf = ru_cf->find("iface")) { 101 | if (ru_cf->find("static") || ru_cf->find("auto")) { 102 | logger::error() 103 | << "Only one of 'iface', 'auto' and 'static' may " 104 | << "be specified."; 105 | return (conf*)NULL; 106 | } 107 | if ((const std::string&)*x_cf == "") { 108 | logger::error() << "'iface' expected an interface name"; 109 | return (conf*)NULL; 110 | } 111 | } else if (ru_cf->find("static")) { 112 | if (ru_cf->find("auto")) { 113 | logger::error() 114 | << "Only one of 'iface', 'auto' and 'static' may " 115 | << "be specified."; 116 | return (conf*)NULL; 117 | } 118 | if (addr.prefix() <= 120) { 119 | logger::warning() 120 | << "Low prefix length (" << addr.prefix() 121 | << " <= 120) when using 'static' method"; 122 | } 123 | } else if (!ru_cf->find("auto")) { 124 | logger::error() 125 | << "You must specify either 'iface', 'auto' or " 126 | << "'static'"; 127 | return (conf*)NULL; 128 | 129 | } 130 | } 131 | } 132 | 133 | return cf; 134 | } 135 | 136 | static bool configure(ptr& cf) 137 | { 138 | ptr x_cf; 139 | 140 | if (!(x_cf = cf->find("route-ttl"))) 141 | route::ttl(30000); 142 | else 143 | route::ttl(*x_cf); 144 | 145 | if (!(x_cf = cf->find("address-ttl"))) 146 | address::ttl(30000); 147 | else 148 | address::ttl(*x_cf); 149 | 150 | std::list > myrules; 151 | 152 | std::vector >::const_iterator p_it; 153 | 154 | std::vector > proxies(cf->find_all("proxy")); 155 | 156 | for (p_it = proxies.begin(); p_it != proxies.end(); p_it++) { 157 | ptr pr_cf = *p_it; 158 | 159 | if (pr_cf->empty()) { 160 | return false; 161 | } 162 | 163 | bool promiscuous = false; 164 | if (!(x_cf = pr_cf->find("promiscuous"))) 165 | promiscuous = false; 166 | else 167 | promiscuous = *x_cf; 168 | 169 | ptr pr = proxy::open(*pr_cf, promiscuous); 170 | if (!pr || pr.is_null() == true) { 171 | return false; 172 | } 173 | 174 | if (!(x_cf = pr_cf->find("router"))) 175 | pr->router(true); 176 | else 177 | pr->router(*x_cf); 178 | 179 | if (!(x_cf = pr_cf->find("autowire"))) 180 | pr->autowire(false); 181 | else 182 | pr->autowire(*x_cf); 183 | 184 | if (!(x_cf = pr_cf->find("keepalive"))) 185 | pr->keepalive(true); 186 | else 187 | pr->keepalive(*x_cf); 188 | 189 | if (!(x_cf = pr_cf->find("retries"))) 190 | pr->retries(3); 191 | else 192 | pr->retries(*x_cf); 193 | 194 | if (!(x_cf = pr_cf->find("ttl"))) 195 | pr->ttl(30000); 196 | else 197 | pr->ttl(*x_cf); 198 | 199 | if (!(x_cf = pr_cf->find("deadtime"))) 200 | pr->deadtime(pr->ttl()); 201 | else 202 | pr->deadtime(*x_cf); 203 | 204 | if (!(x_cf = pr_cf->find("timeout"))) 205 | pr->timeout(500); 206 | else 207 | pr->timeout(*x_cf); 208 | 209 | std::vector >::const_iterator r_it; 210 | 211 | std::vector > rules(pr_cf->find_all("rule")); 212 | 213 | for (r_it = rules.begin(); r_it != rules.end(); r_it++) { 214 | ptr ru_cf =* r_it; 215 | 216 | address addr(*ru_cf); 217 | 218 | bool autovia = false; 219 | if (!(x_cf = ru_cf->find("autovia"))) 220 | autovia = false; 221 | else 222 | autovia = *x_cf; 223 | 224 | if (x_cf = ru_cf->find("iface")) 225 | { 226 | ptr ifa = iface::open_ifd(*x_cf); 227 | if (!ifa || ifa.is_null() == true) { 228 | return false; 229 | } 230 | 231 | ifa->add_parent(pr); 232 | 233 | myrules.push_back(pr->add_rule(addr, ifa, autovia)); 234 | } else if (ru_cf->find("auto")) { 235 | myrules.push_back(pr->add_rule(addr, true)); 236 | } else { 237 | myrules.push_back(pr->add_rule(addr, false)); 238 | } 239 | } 240 | } 241 | 242 | // Print out all the topology 243 | for (std::map >::iterator i_it = iface::_map.begin(); i_it != iface::_map.end(); i_it++) { 244 | ptr ifa = i_it->second; 245 | 246 | logger::debug() << "iface " << ifa->name() << " {"; 247 | 248 | for (std::list >::iterator pit = ifa->serves_begin(); pit != ifa->serves_end(); pit++) { 249 | ptr pr = (*pit); 250 | if (!pr) continue; 251 | 252 | logger::debug() << " " << "proxy " << logger::format("%x", pr.get_pointer()) << " {"; 253 | 254 | for (std::list >::iterator rit = pr->rules_begin(); rit != pr->rules_end(); rit++) { 255 | ptr ru = *rit; 256 | 257 | logger::debug() << " " << "rule " << logger::format("%x", ru.get_pointer()) << " {"; 258 | logger::debug() << " " << "taddr " << ru->addr()<< ";"; 259 | if (ru->is_auto()) 260 | logger::debug() << " " << "auto;"; 261 | else if (!ru->daughter()) 262 | logger::debug() << " " << "static;"; 263 | else 264 | logger::debug() << " " << "iface " << ru->daughter()->name() << ";"; 265 | logger::debug() << " }"; 266 | } 267 | 268 | logger::debug() << " }"; 269 | } 270 | 271 | logger::debug() << " " << "parents {"; 272 | for (std::list >::iterator pit = ifa->parents_begin(); pit != ifa->parents_end(); pit++) { 273 | ptr pr = (*pit); 274 | 275 | logger::debug() << " " << "parent " << logger::format("%x", pr.get_pointer()) << ";"; 276 | } 277 | logger::debug() << " }"; 278 | 279 | logger::debug() << "}"; 280 | } 281 | 282 | return true; 283 | } 284 | 285 | static bool running = true; 286 | 287 | static void exit_ndppd(int sig) 288 | { 289 | logger::error() << "Shutting down..."; 290 | running = 0; 291 | } 292 | 293 | int main(int argc, char* argv[], char* env[]) 294 | { 295 | signal(SIGINT, exit_ndppd); 296 | signal(SIGTERM, exit_ndppd); 297 | 298 | std::string config_path("/etc/ndppd.conf"); 299 | std::string pidfile; 300 | std::string verbosity; 301 | bool daemon = false; 302 | 303 | while (1) { 304 | int c, opt; 305 | 306 | static struct option long_options[] = 307 | { 308 | { "config", 1, 0, 'c' }, 309 | { "daemon", 0, 0, 'd' }, 310 | { "verbose", 1, 0, 'v' }, 311 | { 0, 0, 0, 0} 312 | }; 313 | 314 | c = getopt_long(argc, argv, "c:dp:v", long_options,& opt); 315 | 316 | if (c == -1) 317 | break; 318 | 319 | switch (c) { 320 | case 'c': 321 | config_path = optarg; 322 | break; 323 | 324 | case 'd': 325 | daemon = true; 326 | break; 327 | 328 | case 'p': 329 | pidfile = optarg; 330 | break; 331 | 332 | case 'v': 333 | logger::verbosity(logger::verbosity() + 1); 334 | /*if (optarg) { 335 | if (!logger::verbosity(optarg)) 336 | logger::error() << "Unknown verbosity level '" << optarg << "'"; 337 | } else { 338 | logger::max_pri(LOG_INFO); 339 | }*/ 340 | break; 341 | } 342 | } 343 | 344 | logger::notice() 345 | << "ndppd (NDP Proxy Daemon) version " NDPPD_VERSION << logger::endl 346 | << "Using configuration file '" << config_path << "'"; 347 | 348 | // Load configuration. 349 | 350 | ptr cf = load_config(config_path); 351 | if (cf.is_null()) 352 | return -1; 353 | 354 | if (daemon) { 355 | logger::syslog(true); 356 | 357 | if (daemonize() < 0) 358 | return 1; 359 | } 360 | 361 | if (!configure(cf)) 362 | return -1; 363 | 364 | if (!pidfile.empty()) { 365 | std::ofstream pf; 366 | pf.open(pidfile.c_str(), std::ios::out | std::ios::trunc); 367 | pf << getpid() << std::endl; 368 | pf.close(); 369 | } 370 | 371 | // Time stuff. 372 | 373 | struct timeval t1, t2; 374 | 375 | gettimeofday(&t1, 0); 376 | 377 | #ifdef WITH_ND_NETLINK 378 | netlink_setup(); 379 | #endif 380 | 381 | while (running) { 382 | if (iface::poll_all() < 0) { 383 | if (running) { 384 | logger::error() << "iface::poll_all() failed"; 385 | } 386 | break; 387 | } 388 | 389 | int elapsed_time; 390 | gettimeofday(&t2, 0); 391 | 392 | elapsed_time = 393 | ((t2.tv_sec - t1.tv_sec)* 1000) + 394 | ((t2.tv_usec - t1.tv_usec) / 1000); 395 | 396 | t1.tv_sec = t2.tv_sec; 397 | t1.tv_usec = t2.tv_usec; 398 | 399 | if (rule::any_auto()) 400 | route::update(elapsed_time); 401 | 402 | if (rule::any_iface()) 403 | address::update(elapsed_time); 404 | 405 | session::update_all(elapsed_time); 406 | } 407 | 408 | #ifdef WITH_ND_NETLINK 409 | netlink_teardown(); 410 | #endif 411 | 412 | logger::notice() << "Bye"; 413 | 414 | return 0; 415 | } 416 | 417 | -------------------------------------------------------------------------------- /src/ndppd.h: -------------------------------------------------------------------------------- 1 | // ndppd - NDP Proxy Daemon 2 | // Copyright (C) 2011 Daniel Adolfsson 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program. If not, see . 16 | #pragma once 17 | 18 | #include 19 | #include 20 | 21 | #define NDPPD_NS_BEGIN namespace ndppd { 22 | #define NDPPD_NS_END } 23 | 24 | #define NDPPD_VERSION "0.2.5" 25 | 26 | #include 27 | 28 | #include "ptr.h" 29 | 30 | #include "logger.h" 31 | #include "conf.h" 32 | #include "address.h" 33 | 34 | #include "iface.h" 35 | #include "proxy.h" 36 | #include "session.h" 37 | #include "rule.h" 38 | #include "nd-netlink.h" 39 | -------------------------------------------------------------------------------- /src/proxy.cc: -------------------------------------------------------------------------------- 1 | // ndppd - NDP Proxy Daemon 2 | // Copyright (C) 2011 Daniel Adolfsson 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program. If not, see . 16 | #include 17 | #include 18 | #include 19 | 20 | #include "ndppd.h" 21 | 22 | #include "proxy.h" 23 | #include "route.h" 24 | #include "iface.h" 25 | #include "rule.h" 26 | #include "session.h" 27 | 28 | NDPPD_NS_BEGIN 29 | 30 | static address all_nodes = address("ff02::1"); 31 | 32 | std::list > proxy::_list; 33 | 34 | proxy::proxy() : 35 | _router(true), _ttl(30000), _deadtime(3000), _timeout(500), _autowire(false), _keepalive(true), _promiscuous(false), _retries(3) 36 | { 37 | } 38 | 39 | ptr proxy::find_aunt(const std::string& ifname, const address& taddr) 40 | { 41 | for (std::list >::iterator sit = _list.begin(); 42 | sit != _list.end(); sit++) 43 | { 44 | ptr pr = (*sit); 45 | 46 | bool has_addr = false; 47 | for (std::list >::iterator it = pr->_rules.begin(); it != pr->_rules.end(); it++) { 48 | ptr ru = *it; 49 | 50 | if (ru->addr() == taddr) { 51 | has_addr = true; 52 | break; 53 | } 54 | } 55 | 56 | if (has_addr == false) { 57 | continue; 58 | } 59 | 60 | if (pr->ifa() && pr->ifa()->name() == ifname) 61 | return pr; 62 | } 63 | 64 | return ptr(); 65 | } 66 | 67 | ptr proxy::create(const ptr& ifa, bool promiscuous) 68 | { 69 | ptr pr(new proxy()); 70 | pr->_ptr = pr; 71 | pr->_ifa = ifa; 72 | pr->_promiscuous = promiscuous; 73 | 74 | _list.push_back(pr); 75 | 76 | ifa->add_serves(pr); 77 | 78 | logger::debug() << "proxy::create() if=" << ifa->name(); 79 | 80 | return pr; 81 | } 82 | 83 | ptr proxy::open(const std::string& ifname, bool promiscuous) 84 | { 85 | ptr ifa = iface::open_pfd(ifname, promiscuous); 86 | 87 | if (!ifa) { 88 | return ptr(); 89 | } 90 | 91 | return create(ifa, promiscuous); 92 | } 93 | 94 | ptr proxy::find_or_create_session(const address& taddr) 95 | { 96 | // Let's check this proxy's list of sessions to see if we can 97 | // find one with the same target address. 98 | 99 | for (std::list >::iterator sit = _sessions.begin(); 100 | sit != _sessions.end(); sit++) { 101 | 102 | if ((*sit)->taddr() == taddr) 103 | return (*sit); 104 | } 105 | 106 | ptr se; 107 | 108 | // Since we couldn't find a session that matched, we'll try to find 109 | // a matching rule instead, and then set up a new session. 110 | 111 | for (std::list >::iterator it = _rules.begin(); 112 | it != _rules.end(); it++) { 113 | ptr ru = *it; 114 | 115 | logger::debug() << "checking " << ru->addr() << " against " << taddr; 116 | 117 | if (ru->addr() == taddr) { 118 | if (!se) { 119 | se = session::create(_ptr, taddr, _autowire, _keepalive, _retries); 120 | } 121 | 122 | if (ru->is_auto()) { 123 | ptr rt = route::find(taddr); 124 | 125 | if (rt->ifname() == _ifa->name()) { 126 | logger::debug() << "skipping route since it's using interface " << rt->ifname(); 127 | } else { 128 | ptr ifa = rt->ifa(); 129 | 130 | if (ifa && (ifa != ru->daughter())) { 131 | se->add_iface(ifa); 132 | } 133 | } 134 | } else if (!ru->daughter()) { 135 | // This rule doesn't have an interface, and thus we'll consider 136 | // it "static" and immediately send the response. 137 | se->handle_advert(); 138 | return se; 139 | 140 | } else { 141 | 142 | ptr ifa = ru->daughter(); 143 | se->add_iface(ifa); 144 | 145 | #ifdef WITH_ND_NETLINK 146 | if (if_addr_find(ifa->name(), &taddr.const_addr())) { 147 | logger::debug() << "Sending NA out " << ifa->name(); 148 | se->add_iface(_ifa); 149 | se->handle_advert(); 150 | } 151 | #endif 152 | } 153 | } 154 | } 155 | 156 | if (se) { 157 | _sessions.push_back(se); 158 | } 159 | 160 | return se; 161 | } 162 | 163 | void proxy::handle_advert(const address& saddr, const address& taddr, const std::string& ifname, bool use_via) 164 | { 165 | // If a session exists then process the advert in the context of the session 166 | for (std::list >::iterator s_it = _sessions.begin(); 167 | s_it != _sessions.end(); s_it++) 168 | { 169 | const ptr sess = *s_it; 170 | 171 | if ((sess->taddr() == taddr)) { 172 | sess->handle_advert(saddr, ifname, use_via); 173 | } 174 | } 175 | } 176 | 177 | void proxy::handle_stateless_advert(const address& saddr, const address& taddr, const std::string& ifname, bool use_via) 178 | { 179 | logger::debug() 180 | << "proxy::handle_stateless_advert() proxy=" << (ifa() ? ifa()->name() : "null") << ", taddr=" << taddr.to_string() << ", ifname=" << ifname; 181 | 182 | ptr se = find_or_create_session(taddr); 183 | if (!se) return; 184 | 185 | if (_autowire == true && se->status() == session::WAITING) { 186 | se->handle_auto_wire(saddr, ifname, use_via); 187 | } 188 | } 189 | 190 | void proxy::handle_solicit(const address& saddr, const address& taddr, const std::string& ifname) 191 | { 192 | logger::debug() 193 | << "proxy::handle_solicit()"; 194 | 195 | // Otherwise find or create a session to scan for this address 196 | ptr se = find_or_create_session(taddr); 197 | if (!se) return; 198 | 199 | // Touching the session will cause an NDP advert to be transmitted to all 200 | // the daughters 201 | se->touch(); 202 | 203 | // If our session is confirmed then we can respoond with an advert otherwise 204 | // subscribe so that if it does become active we can notify everyone 205 | if (saddr != taddr) { 206 | switch (se->status()) { 207 | case session::WAITING: 208 | case session::INVALID: 209 | se->add_pending(saddr); 210 | break; 211 | 212 | case session::VALID: 213 | case session::RENEWING: 214 | se->send_advert(saddr); 215 | break; 216 | } 217 | } 218 | } 219 | 220 | ptr proxy::add_rule(const address& addr, const ptr& ifa, bool autovia) 221 | { 222 | ptr ru(rule::create(_ptr, addr, ifa)); 223 | ru->autovia(autovia); 224 | _rules.push_back(ru); 225 | return ru; 226 | } 227 | 228 | ptr proxy::add_rule(const address& addr, bool aut) 229 | { 230 | ptr ru(rule::create(_ptr, addr, aut)); 231 | _rules.push_back(ru); 232 | return ru; 233 | } 234 | 235 | std::list >::iterator proxy::rules_begin() 236 | { 237 | return _rules.begin(); 238 | } 239 | 240 | std::list >::iterator proxy::rules_end() 241 | { 242 | return _rules.end(); 243 | } 244 | 245 | void proxy::remove_session(const ptr& se) 246 | { 247 | _sessions.remove(se); 248 | } 249 | 250 | const ptr& proxy::ifa() const 251 | { 252 | return _ifa; 253 | } 254 | 255 | bool proxy::promiscuous() const 256 | { 257 | return _promiscuous; 258 | } 259 | 260 | bool proxy::router() const 261 | { 262 | return _router; 263 | } 264 | 265 | void proxy::router(bool val) 266 | { 267 | _router = val; 268 | } 269 | 270 | bool proxy::autowire() const 271 | { 272 | return _autowire; 273 | } 274 | 275 | void proxy::autowire(bool val) 276 | { 277 | _autowire = val; 278 | } 279 | 280 | int proxy::retries() const 281 | { 282 | return _retries; 283 | } 284 | 285 | void proxy::retries(int val) 286 | { 287 | _retries = val; 288 | } 289 | 290 | bool proxy::keepalive() const 291 | { 292 | return _keepalive; 293 | } 294 | 295 | void proxy::keepalive(bool val) 296 | { 297 | _keepalive = val; 298 | } 299 | 300 | int proxy::ttl() const 301 | { 302 | return _ttl; 303 | } 304 | 305 | void proxy::ttl(int val) 306 | { 307 | _ttl = (val >= 0) ? val : 30000; 308 | } 309 | 310 | int proxy::deadtime() const 311 | { 312 | return _deadtime; 313 | } 314 | 315 | void proxy::deadtime(int val) 316 | { 317 | _deadtime = (val >= 0) ? val : 30000; 318 | } 319 | 320 | int proxy::timeout() const 321 | { 322 | return _timeout; 323 | } 324 | 325 | void proxy::timeout(int val) 326 | { 327 | _timeout = (val >= 0) ? val : 500; 328 | } 329 | 330 | NDPPD_NS_END 331 | 332 | -------------------------------------------------------------------------------- /src/proxy.h: -------------------------------------------------------------------------------- 1 | // ndppd - NDP Proxy Daemon 2 | // Copyright (C) 2011 Daniel Adolfsson 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program. If not, see . 16 | #pragma once 17 | 18 | #include 19 | #include 20 | #include 21 | 22 | #include 23 | 24 | #include "ndppd.h" 25 | 26 | NDPPD_NS_BEGIN 27 | 28 | class iface; 29 | class rule; 30 | 31 | class proxy { 32 | public: 33 | static ptr create(const ptr& ifa, bool promiscuous); 34 | 35 | static ptr find_aunt(const std::string& ifname, const address& taddr); 36 | 37 | static ptr open(const std::string& ifn, bool promiscuous); 38 | 39 | ptr find_or_create_session(const address& taddr); 40 | 41 | void handle_advert(const address& saddr, const address& taddr, const std::string& ifname, bool use_via); 42 | 43 | void handle_stateless_advert(const address& saddr, const address& taddr, const std::string& ifname, bool use_via); 44 | 45 | void handle_solicit(const address& saddr, const address& taddr, const std::string& ifname); 46 | 47 | void remove_session(const ptr& se); 48 | 49 | ptr add_rule(const address& addr, const ptr& ifa, bool autovia); 50 | 51 | ptr add_rule(const address& addr, bool aut = false); 52 | 53 | std::list >::iterator rules_begin(); 54 | 55 | std::list >::iterator rules_end(); 56 | 57 | const ptr& ifa() const; 58 | 59 | bool promiscuous() const; 60 | 61 | bool router() const; 62 | 63 | void router(bool val); 64 | 65 | bool autowire() const; 66 | 67 | void autowire(bool val); 68 | 69 | int retries() const; 70 | 71 | void retries(int val); 72 | 73 | bool keepalive() const; 74 | 75 | void keepalive(bool val); 76 | 77 | int timeout() const; 78 | 79 | void timeout(int val); 80 | 81 | int ttl() const; 82 | 83 | void ttl(int val); 84 | 85 | int deadtime() const; 86 | 87 | void deadtime(int val); 88 | 89 | private: 90 | static std::list > _list; 91 | 92 | weak_ptr _ptr; 93 | 94 | ptr _ifa; 95 | 96 | std::list > _rules; 97 | 98 | std::list > _sessions; 99 | 100 | bool _promiscuous; 101 | 102 | bool _router; 103 | 104 | bool _autowire; 105 | 106 | int _retries; 107 | 108 | bool _keepalive; 109 | 110 | int _ttl, _deadtime, _timeout; 111 | 112 | proxy(); 113 | }; 114 | 115 | NDPPD_NS_END 116 | -------------------------------------------------------------------------------- /src/ptr.h: -------------------------------------------------------------------------------- 1 | // ndppd - NDP Proxy Daemon 2 | // Copyright (C) 2011 Daniel Adolfsson 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program. If not, see . 16 | #pragma once 17 | 18 | #include 19 | 20 | #include "ndppd.h" 21 | #include "logger.h" 22 | 23 | NDPPD_NS_BEGIN 24 | 25 | class invalid_pointer : public std::exception { 26 | public: 27 | invalid_pointer() throw() {}; 28 | }; 29 | 30 | template 31 | class weak_ptr; 32 | 33 | // This template class simplifies the usage of pointers. It's basically 34 | // a reference-counting smart pointer that supports both weak and 35 | // strong references. 36 | 37 | template 38 | class ptr { 39 | template 40 | friend class ptr; 41 | 42 | struct ptr_ref { 43 | T* ptr; 44 | int wc, sc; 45 | }; 46 | 47 | protected: 48 | bool _weak; 49 | 50 | ptr_ref* _ref; 51 | 52 | void acquire(ptr_ref* ref) 53 | { 54 | if (_ref) { 55 | release(); 56 | } 57 | 58 | if (ref && !ref->sc) { 59 | throw new invalid_pointer; 60 | } 61 | 62 | if (_ref = ref) { 63 | if (_weak) { 64 | _ref->wc++; 65 | } else { 66 | _ref->sc++; 67 | } 68 | } 69 | } 70 | 71 | void acquire(void* ptr) 72 | { 73 | if (!ptr) return; 74 | 75 | _ref = new ptr_ref(); 76 | _ref->ptr = (T*)ptr; 77 | _ref->wc = !!_weak; 78 | _ref->sc = !_weak; 79 | } 80 | 81 | void release() 82 | { 83 | if (!_ref) { 84 | return; 85 | } 86 | 87 | //logger::debug() 88 | // << "ptr::release() _ref=" << logger::format("%x", _ref) 89 | // << ", _ref->wc=" << _ref->wc << ", _ref->sc=" << _ref->sc 90 | // << ", _weak=" << (_weak ? "yes" : "no"); 91 | 92 | if (_weak) { 93 | assert(_ref->wc > 0); 94 | _ref->wc--; 95 | } else { 96 | assert(_ref->sc > 0); 97 | if (!--_ref->sc && _ref->ptr) { 98 | T* ptr = _ref->ptr; 99 | _ref->ptr = 0; 100 | _ref->wc++; 101 | delete ptr; 102 | _ref->wc--; 103 | } 104 | } 105 | 106 | /*if (!_weak && !_ref->sc && _ref->ptr) { 107 | T* ptr = (T*)(_ref->ptr); 108 | _ref->ptr = 0; 109 | delete ptr; 110 | }*/ 111 | 112 | if (!_ref->sc && !_ref->wc) { 113 | delete _ref; 114 | } 115 | 116 | _ref = 0; 117 | } 118 | 119 | template 120 | void acquire(const ptr& ptr) 121 | { 122 | acquire(ptr._ref); 123 | } 124 | 125 | public: 126 | ptr(bool weak = false) : 127 | _weak(weak), _ref(0) 128 | { 129 | } 130 | 131 | ptr(T* p, bool weak = false) : 132 | _weak(weak), _ref(0) 133 | { 134 | acquire(p); 135 | } 136 | 137 | ptr(const ptr& p, bool weak = false) : 138 | _weak(weak), _ref(0) 139 | { 140 | acquire(p._ref); 141 | } 142 | 143 | ptr(const weak_ptr& p, bool weak = false) : 144 | _weak(weak), _ref(0) 145 | { 146 | acquire(p._ref); 147 | } 148 | 149 | template 150 | ptr(const ptr& p, bool weak = false) : 151 | _weak(weak), _ref(0) 152 | { 153 | T* x = (U*)0; 154 | acquire(p._ref); 155 | } 156 | 157 | template 158 | ptr(const weak_ptr& p, bool weak = false) : 159 | _weak(weak), _ref(0) 160 | { 161 | T* x = (U*)0; 162 | acquire(p._ref); 163 | } 164 | 165 | ~ptr() 166 | { 167 | release(); 168 | } 169 | 170 | void operator=(T* p) 171 | { 172 | acquire(p); 173 | } 174 | 175 | ptr& operator=(const ptr& p) 176 | { 177 | acquire(p); 178 | return* this; 179 | } 180 | 181 | bool operator==(const ptr& other) const 182 | { 183 | return other._ref == _ref; 184 | } 185 | 186 | bool operator!=(const ptr& other) const 187 | { 188 | return other._ref != _ref; 189 | } 190 | 191 | bool is_null() const 192 | { 193 | return !_ref || !_ref->ptr; 194 | } 195 | 196 | T& operator*() const 197 | { 198 | return* get_pointer(); 199 | } 200 | 201 | T* operator->() const 202 | { 203 | return get_pointer(); 204 | } 205 | 206 | operator T*() const 207 | { 208 | return get_pointer(); 209 | } 210 | 211 | operator bool() const 212 | { 213 | return !is_null(); 214 | } 215 | 216 | void reset(T* p = 0) 217 | { 218 | acquire(p); 219 | } 220 | 221 | T* get_pointer() const 222 | { 223 | if (!_ref || !_ref->ptr) { 224 | throw new invalid_pointer; 225 | } 226 | 227 | return static_cast(_ref->ptr); 228 | } 229 | }; 230 | 231 | template 232 | class weak_ptr : public ptr { 233 | public: 234 | weak_ptr() : 235 | ptr(true) 236 | { 237 | } 238 | 239 | weak_ptr(T* p) : 240 | ptr(p, true) 241 | { 242 | } 243 | 244 | weak_ptr(const ptr& p) : 245 | ptr(p, true) 246 | { 247 | } 248 | 249 | weak_ptr(const weak_ptr& p) : 250 | ptr(p, true) 251 | { 252 | } 253 | 254 | template 255 | weak_ptr(const ptr& p) : 256 | ptr(p, true) 257 | { 258 | } 259 | 260 | template 261 | weak_ptr(const weak_ptr& p) : 262 | ptr(p, true) 263 | { 264 | } 265 | }; 266 | 267 | NDPPD_NS_END 268 | 269 | 270 | -------------------------------------------------------------------------------- /src/route.cc: -------------------------------------------------------------------------------- 1 | // ndppd - NDP Proxy Daemon 2 | // Copyright (C) 2011 Daniel Adolfsson 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program. If not, see . 16 | #include 17 | #include 18 | #include 19 | 20 | #include "ndppd.h" 21 | #include "route.h" 22 | 23 | NDPPD_NS_BEGIN 24 | 25 | std::list > route::_routes; 26 | 27 | int route::_ttl; 28 | 29 | int route::_c_ttl; 30 | 31 | route::route(const address& addr, const std::string& ifname) : 32 | _addr(addr), _ifname(ifname) 33 | { 34 | } 35 | 36 | size_t route::hexdec(const char* str, unsigned char* buf, size_t size) 37 | { 38 | for (size_t i = 0; ; i++) { 39 | if (i >= size) { 40 | return i; 41 | } 42 | 43 | char c1 = tolower(str[i* 2]), c2 = tolower(str[i* 2 + 1]); 44 | 45 | if (!isxdigit(c1) || !isxdigit(c2)) { 46 | return i; 47 | } 48 | 49 | if ((c1 >= '0') && (c1 <= '9')) { 50 | buf[i] = (c1 - '0') << 4; 51 | } else { 52 | buf[i] = ((c1 - 'a') + 10) << 4; 53 | } 54 | 55 | if ((c2 >= '0') && (c2 <= '9')) { 56 | buf[i] |= c2 - '0'; 57 | } else { 58 | buf[i] |= (c2 - 'a') + 10; 59 | } 60 | } 61 | } 62 | 63 | std::string route::token(const char* str) 64 | { 65 | while (*str && isspace(*str)) { 66 | str++; 67 | } 68 | 69 | if (!*str) { 70 | return ""; 71 | } 72 | 73 | std::stringstream ss; 74 | 75 | while (*str && !isspace(*str)) { 76 | ss <<* str++; 77 | } 78 | 79 | return ss.str(); 80 | } 81 | 82 | void route::load(const std::string& path) 83 | { 84 | // Hack to make sure the interfaces are not freed prematurely. 85 | std::list > tmp_routes(_routes); 86 | _routes.clear(); 87 | 88 | logger::debug() << "reading routes"; 89 | 90 | try { 91 | std::ifstream ifs; 92 | ifs.exceptions(std::ifstream::badbit | std::ifstream::failbit); 93 | ifs.open(path.c_str(), std::ios::in); 94 | ifs.exceptions(std::ifstream::badbit); 95 | 96 | while (!ifs.eof()) { 97 | char buf[1024]; 98 | ifs.getline(buf, sizeof(buf)); 99 | 100 | if (ifs.gcount() < 149) { 101 | continue; 102 | } 103 | 104 | address addr; 105 | 106 | unsigned char pfx; 107 | 108 | if (route::hexdec(buf, (unsigned char* )&addr.addr(), 16) != 16) { 109 | // TODO: Warn here? 110 | continue; 111 | } 112 | 113 | if (route::hexdec(buf + 33,& pfx, 1) != 1) { 114 | // TODO: Warn here? 115 | continue; 116 | } 117 | 118 | addr.prefix((int)pfx); 119 | 120 | route::create(addr, route::token(buf + 141)); 121 | } 122 | } catch (std::ifstream::failure e) { 123 | logger::warning() << "Failed to parse IPv6 routing data from '" << path << "'"; 124 | logger::error() << e.what(); 125 | } 126 | } 127 | 128 | void route::update(int elapsed_time) 129 | { 130 | if ((_c_ttl -= elapsed_time) <= 0) { 131 | load("/proc/net/ipv6_route"); 132 | _c_ttl = _ttl; 133 | } 134 | } 135 | 136 | ptr route::create(const address& addr, const std::string& ifname) 137 | { 138 | ptr rt(new route(addr, ifname)); 139 | // logger::debug() << "route::create() addr=" << addr << ", ifname=" << ifname; 140 | _routes.push_back(rt); 141 | return rt; 142 | } 143 | 144 | ptr route::find(const address& addr) 145 | { 146 | for (std::list >::iterator it = _routes.begin(); 147 | it != _routes.end(); it++) { 148 | if ((*it)->addr() == addr) 149 | return *it; 150 | } 151 | 152 | return ptr(); 153 | } 154 | 155 | ptr route::find_and_open(const address& addr) 156 | { 157 | ptr rt; 158 | 159 | if (rt = find(addr)) { 160 | return rt->ifa(); 161 | } 162 | 163 | return ptr(); 164 | } 165 | 166 | const std::string& route::ifname() const 167 | { 168 | return _ifname; 169 | } 170 | 171 | ptr route::ifa() 172 | { 173 | if (!_ifa) { 174 | logger::debug() << "router::ifa() opening interface '" << _ifname << "'"; 175 | return _ifa = iface::open_ifd(_ifname); 176 | } 177 | 178 | return ptr(); 179 | } 180 | 181 | const address& route::addr() const 182 | { 183 | return _addr; 184 | } 185 | 186 | int route::ttl() 187 | { 188 | return _ttl; 189 | } 190 | 191 | void route::ttl(int ttl) 192 | { 193 | _ttl = ttl; 194 | } 195 | 196 | NDPPD_NS_END 197 | 198 | -------------------------------------------------------------------------------- /src/route.h: -------------------------------------------------------------------------------- 1 | // ndppd - NDP Proxy Daemon 2 | // Copyright (C) 2011 Daniel Adolfsson 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program. If not, see . 16 | #pragma once 17 | 18 | #include 19 | #include 20 | #include 21 | 22 | #include "ndppd.h" 23 | 24 | NDPPD_NS_BEGIN 25 | 26 | class route { 27 | public: 28 | static ptr create(const address& addr, const std::string& ifname); 29 | 30 | static ptr find(const address& addr); 31 | 32 | static ptr find_and_open(const address& addr); 33 | 34 | static void load(const std::string& path); 35 | 36 | static void update(int elapsed_time); 37 | 38 | static int ttl(); 39 | 40 | static void ttl(int ttl); 41 | 42 | const std::string& ifname() const; 43 | 44 | const address& addr() const; 45 | 46 | ptr ifa(); 47 | 48 | route(const address& addr, const std::string& ifname); 49 | 50 | static size_t hexdec(const char* str, unsigned char* buf, size_t size); 51 | 52 | static std::string token(const char* str); 53 | 54 | private: 55 | static int _ttl; 56 | 57 | static int _c_ttl; 58 | 59 | address _addr; 60 | 61 | std::string _ifname; 62 | 63 | ptr _ifa; 64 | 65 | static std::list > _routes; 66 | 67 | }; 68 | 69 | NDPPD_NS_END 70 | -------------------------------------------------------------------------------- /src/rule.cc: -------------------------------------------------------------------------------- 1 | // ndppd - NDP Proxy Daemon 2 | // Copyright (C) 2011 Daniel Adolfsson 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program. If not, see . 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include "ndppd.h" 22 | #include "rule.h" 23 | #include "proxy.h" 24 | #include "iface.h" 25 | 26 | NDPPD_NS_BEGIN 27 | 28 | std::vector interfaces; 29 | 30 | bool rule::_any_aut = false; 31 | 32 | bool rule::_any_iface = false; 33 | 34 | bool rule::_any_static = false; 35 | 36 | rule::rule() 37 | { 38 | } 39 | 40 | ptr rule::create(const ptr& pr, const address& addr, const ptr& ifa) 41 | { 42 | ptr ru(new rule()); 43 | ru->_ptr = ru; 44 | ru->_pr = pr; 45 | ru->_daughter = ifa; 46 | ru->_addr = addr; 47 | ru->_aut = false; 48 | _any_iface = true; 49 | unsigned int ifindex; 50 | 51 | ifindex = if_nametoindex(pr->ifa()->name().c_str()); 52 | #ifdef WITH_ND_NETLINK 53 | if_add_to_list(ifindex, pr->ifa()); 54 | #endif 55 | ifindex = if_nametoindex(ifa->name().c_str()); 56 | #ifdef WITH_ND_NETLINK 57 | if_add_to_list(ifindex, ifa); 58 | #endif 59 | 60 | logger::debug() << "rule::create() if=" << pr->ifa()->name() << ", slave=" << ifa->name() << ", addr=" << addr; 61 | 62 | return ru; 63 | } 64 | 65 | ptr rule::create(const ptr& pr, const address& addr, bool aut) 66 | { 67 | ptr ru(new rule()); 68 | ru->_ptr = ru; 69 | ru->_pr = pr; 70 | ru->_addr = addr; 71 | ru->_aut = aut; 72 | _any_aut = _any_aut || aut; 73 | 74 | if (aut == false) 75 | _any_static = true; 76 | 77 | logger::debug() 78 | << "rule::create() if=" << pr->ifa()->name().c_str() << ", addr=" << addr 79 | << ", auto=" << (aut ? "yes" : "no"); 80 | 81 | return ru; 82 | } 83 | 84 | const address& rule::addr() const 85 | { 86 | return _addr; 87 | } 88 | 89 | ptr rule::daughter() const 90 | { 91 | return _daughter; 92 | } 93 | 94 | bool rule::is_auto() const 95 | { 96 | return _aut; 97 | } 98 | 99 | bool rule::autovia() const 100 | { 101 | return _autovia; 102 | } 103 | 104 | void rule::autovia(bool val) 105 | { 106 | _autovia = val; 107 | } 108 | 109 | bool rule::any_auto() 110 | { 111 | return _any_aut; 112 | } 113 | 114 | bool rule::any_iface() 115 | { 116 | return _any_iface; 117 | } 118 | 119 | bool rule::any_static() 120 | { 121 | return _any_static; 122 | } 123 | 124 | bool rule::check(const address& addr) const 125 | { 126 | return _addr == addr; 127 | } 128 | 129 | NDPPD_NS_END 130 | -------------------------------------------------------------------------------- /src/rule.h: -------------------------------------------------------------------------------- 1 | // ndppd - NDP Proxy Daemon 2 | // Copyright (C) 2011 Daniel Adolfsson 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program. If not, see . 16 | #pragma once 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include 24 | 25 | #include "ndppd.h" 26 | 27 | NDPPD_NS_BEGIN 28 | 29 | class iface; 30 | class proxy; 31 | 32 | class rule { 33 | public: 34 | static ptr create(const ptr& pr, const address& addr, const ptr& ifa); 35 | 36 | static ptr create(const ptr& pr, const address& addr, bool stc = true); 37 | 38 | const address& addr() const; 39 | 40 | ptr daughter() const; 41 | 42 | bool is_auto() const; 43 | 44 | bool check(const address& addr) const; 45 | 46 | static bool any_auto(); 47 | 48 | static bool any_static(); 49 | 50 | static bool any_iface(); 51 | 52 | bool autovia() const; 53 | 54 | void autovia(bool val); 55 | 56 | private: 57 | weak_ptr _ptr; 58 | 59 | weak_ptr _pr; 60 | 61 | ptr _daughter; 62 | 63 | address _addr; 64 | 65 | bool _aut; 66 | 67 | static bool _any_aut; 68 | 69 | static bool _any_static; 70 | 71 | static bool _any_iface; 72 | 73 | bool _autovia; 74 | 75 | rule(); 76 | }; 77 | 78 | class interface { 79 | public: 80 | // List of IPv6 addresses on this interface 81 | std::list
addresses; 82 | 83 | // Index of this interface 84 | int ifindex; 85 | 86 | // Name of this interface. 87 | std::string _name; 88 | 89 | }; 90 | 91 | extern std::vector interfaces; 92 | 93 | NDPPD_NS_END 94 | -------------------------------------------------------------------------------- /src/session.cc: -------------------------------------------------------------------------------- 1 | // ndppd - NDP Proxy Daemon 2 | // Copyright (C) 2011 Daniel Adolfsson 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program. If not, see . 16 | #include 17 | #include 18 | 19 | #include "ndppd.h" 20 | #include "proxy.h" 21 | #include "iface.h" 22 | #include "session.h" 23 | 24 | NDPPD_NS_BEGIN 25 | 26 | std::list > session::_sessions; 27 | 28 | static address all_nodes = address("ff02::1"); 29 | 30 | void session::update_all(int elapsed_time) 31 | { 32 | for (std::list >::iterator it = _sessions.begin(); 33 | it != _sessions.end(); ) { 34 | if (!*it) { 35 | _sessions.erase(it++); 36 | continue; 37 | } 38 | 39 | ptr se = *it++; 40 | 41 | if ((se->_ttl -= elapsed_time) >= 0) { 42 | continue; 43 | } 44 | 45 | switch (se->_status) { 46 | 47 | case session::WAITING: 48 | if (se->_fails < se->_retries) { 49 | logger::debug() << "session will keep trying [taddr=" << se->_taddr << "]"; 50 | 51 | se->_ttl = se->_pr->timeout(); 52 | se->_fails++; 53 | 54 | // Send another solicit 55 | se->send_solicit(); 56 | } else { 57 | 58 | logger::debug() << "session is now invalid [taddr=" << se->_taddr << "]"; 59 | 60 | se->_status = session::INVALID; 61 | se->_ttl = se->_pr->deadtime(); 62 | } 63 | break; 64 | 65 | case session::RENEWING: 66 | logger::debug() << "session is became invalid [taddr=" << se->_taddr << "]"; 67 | 68 | if (se->_fails < se->_retries) { 69 | se->_ttl = se->_pr->timeout(); 70 | se->_fails++; 71 | 72 | // Send another solicit 73 | se->send_solicit(); 74 | } else { 75 | se->_pr->remove_session(se); 76 | } 77 | break; 78 | 79 | case session::VALID: 80 | if (se->touched() == true || 81 | se->keepalive() == true) 82 | { 83 | logger::debug() << "session is renewing [taddr=" << se->_taddr << "]"; 84 | se->_status = session::RENEWING; 85 | se->_ttl = se->_pr->timeout(); 86 | se->_fails = 0; 87 | se->_touched = false; 88 | 89 | // Send another solicit to make sure the route is still valid 90 | se->send_solicit(); 91 | } else { 92 | se->_pr->remove_session(se); 93 | } 94 | break; 95 | 96 | default: 97 | se->_pr->remove_session(se); 98 | } 99 | } 100 | } 101 | 102 | session::~session() 103 | { 104 | logger::debug() << "session::~session() this=" << logger::format("%x", this); 105 | 106 | if (_wired == true) { 107 | for (std::list >::iterator it = _ifaces.begin(); 108 | it != _ifaces.end(); it++) { 109 | handle_auto_unwire((*it)->name()); 110 | } 111 | } 112 | } 113 | 114 | ptr session::create(const ptr& pr, const address& taddr, bool auto_wire, bool keepalive, int retries) 115 | { 116 | ptr se(new session()); 117 | 118 | se->_ptr = se; 119 | se->_pr = pr; 120 | se->_taddr = taddr; 121 | se->_autowire = auto_wire; 122 | se->_keepalive = keepalive; 123 | se->_retries = retries; 124 | se->_wired = false; 125 | se->_ttl = pr->ttl(); 126 | se->_touched = false; 127 | 128 | _sessions.push_back(se); 129 | 130 | logger::debug() 131 | << "session::create() pr=" << logger::format("%x", (proxy* )pr) << ", proxy=" << ((pr->ifa()) ? pr->ifa()->name() : "null") 132 | << ", taddr=" << taddr << " =" << logger::format("%x", (session* )se); 133 | 134 | return se; 135 | } 136 | 137 | void session::add_iface(const ptr& ifa) 138 | { 139 | if (std::find(_ifaces.begin(), _ifaces.end(), ifa) != _ifaces.end()) 140 | return; 141 | 142 | _ifaces.push_back(ifa); 143 | } 144 | 145 | void session::add_pending(const address& addr) 146 | { 147 | for (std::list >::iterator ad = _pending.begin(); ad != _pending.end(); ad++) { 148 | if (addr == (*ad)) 149 | return; 150 | } 151 | 152 | _pending.push_back(new address(addr)); 153 | } 154 | 155 | void session::send_solicit() 156 | { 157 | logger::debug() << "session::send_solicit() (_ifaces.size() = " << _ifaces.size() << ")"; 158 | 159 | for (std::list >::iterator it = _ifaces.begin(); 160 | it != _ifaces.end(); it++) { 161 | logger::debug() << " - " << (*it)->name(); 162 | (*it)->write_solicit(_taddr); 163 | } 164 | } 165 | 166 | void session::touch() 167 | { 168 | if (_touched == false) 169 | { 170 | _touched = true; 171 | 172 | if (status() == session::WAITING || status() == session::INVALID) { 173 | _ttl = _pr->timeout(); 174 | 175 | logger::debug() << "session is now probing [taddr=" << _taddr << "]"; 176 | 177 | send_solicit(); 178 | } 179 | } 180 | } 181 | 182 | void session::send_advert(const address& daddr) 183 | { 184 | _pr->ifa()->write_advert(daddr, _taddr, _pr->router()); 185 | } 186 | 187 | void session::handle_auto_wire(const address& saddr, const std::string& ifname, bool use_via) 188 | { 189 | if (_wired == true && (_wired_via.is_empty() || _wired_via == saddr)) 190 | return; 191 | 192 | logger::debug() 193 | << "session::handle_auto_wire() taddr=" << _taddr << ", ifname=" << ifname; 194 | 195 | if (use_via == true && 196 | _taddr != saddr && 197 | saddr.is_unicast() == true && 198 | saddr.is_multicast() == false) 199 | { 200 | std::stringstream route_cmd; 201 | route_cmd << "ip"; 202 | route_cmd << " " << "-6"; 203 | route_cmd << " " << "route"; 204 | route_cmd << " " << "replace"; 205 | route_cmd << " " << std::string(saddr); 206 | route_cmd << " " << "dev"; 207 | route_cmd << " " << ifname; 208 | 209 | logger::debug() 210 | << "session::system(" << route_cmd.str() << ")"; 211 | 212 | system(route_cmd.str().c_str()); 213 | 214 | _wired_via = saddr; 215 | } 216 | else 217 | _wired_via.reset(); 218 | 219 | { 220 | std::stringstream route_cmd; 221 | route_cmd << "ip"; 222 | route_cmd << " " << "-6"; 223 | route_cmd << " " << "route"; 224 | route_cmd << " " << "replace"; 225 | route_cmd << " " << std::string(_taddr); 226 | if (_wired_via.is_empty() == false) { 227 | route_cmd << " " << "via"; 228 | route_cmd << " " << std::string(_wired_via); 229 | } 230 | route_cmd << " " << "dev"; 231 | route_cmd << " " << ifname; 232 | 233 | logger::debug() 234 | << "session::system(" << route_cmd.str() << ")"; 235 | 236 | system(route_cmd.str().c_str()); 237 | } 238 | 239 | _wired = true; 240 | } 241 | 242 | void session::handle_auto_unwire(const std::string& ifname) 243 | { 244 | logger::debug() 245 | << "session::handle_auto_unwire() taddr=" << _taddr << ", ifname=" << ifname; 246 | 247 | { 248 | std::stringstream route_cmd; 249 | route_cmd << "ip"; 250 | route_cmd << " " << "-6"; 251 | route_cmd << " " << "route"; 252 | route_cmd << " " << "flush"; 253 | route_cmd << " " << std::string(_taddr); 254 | if (_wired_via.is_empty() == false) { 255 | route_cmd << " " << "via"; 256 | route_cmd << " " << std::string(_wired_via); 257 | } 258 | route_cmd << " " << "dev"; 259 | route_cmd << " " << ifname; 260 | 261 | logger::debug() 262 | << "session::system(" << route_cmd.str() << ")"; 263 | 264 | system(route_cmd.str().c_str()); 265 | } 266 | 267 | if (_wired_via.is_empty() == false) { 268 | std::stringstream route_cmd; 269 | route_cmd << "ip"; 270 | route_cmd << " " << "-6"; 271 | route_cmd << " " << "route"; 272 | route_cmd << " " << "flush"; 273 | route_cmd << " " << std::string(_wired_via); 274 | route_cmd << " " << "dev"; 275 | route_cmd << " " << ifname; 276 | 277 | logger::debug() 278 | << "session::system(" << route_cmd.str() << ")"; 279 | 280 | system(route_cmd.str().c_str()); 281 | } 282 | 283 | _wired = false; 284 | _wired_via.reset(); 285 | } 286 | 287 | void session::handle_advert(const address& saddr, const std::string& ifname, bool use_via) 288 | { 289 | if (_autowire == true && _status == WAITING) { 290 | handle_auto_wire(saddr, ifname, use_via); 291 | } 292 | 293 | handle_advert(); 294 | } 295 | 296 | 297 | void session::handle_advert() 298 | { 299 | logger::debug() 300 | << "session::handle_advert() taddr=" << _taddr << ", ttl=" << _pr->ttl(); 301 | 302 | if (_status != VALID) { 303 | _status = VALID; 304 | 305 | logger::debug() << "session is active [taddr=" << _taddr << "]"; 306 | } 307 | 308 | _ttl = _pr->ttl(); 309 | _fails = 0; 310 | 311 | if (!_pending.empty()) { 312 | for (std::list >::iterator ad = _pending.begin(); 313 | ad != _pending.end(); ad++) { 314 | ptr
addr = (*ad); 315 | logger::debug() << " - forward to " << addr; 316 | 317 | send_advert(addr); 318 | } 319 | 320 | _pending.clear(); 321 | } 322 | } 323 | 324 | const address& session::taddr() const 325 | { 326 | return _taddr; 327 | } 328 | 329 | bool session::autowire() const 330 | { 331 | return _autowire; 332 | } 333 | 334 | bool session::keepalive() const 335 | { 336 | return _keepalive; 337 | } 338 | 339 | int session::retries() const 340 | { 341 | return _retries; 342 | } 343 | 344 | int session::fails() const 345 | { 346 | return _fails; 347 | } 348 | 349 | bool session::wired() const 350 | { 351 | return _wired; 352 | } 353 | 354 | bool session::touched() const 355 | { 356 | return _touched; 357 | } 358 | 359 | int session::status() const 360 | { 361 | return _status; 362 | } 363 | 364 | void session::status(int val) 365 | { 366 | _status = val; 367 | } 368 | 369 | NDPPD_NS_END 370 | -------------------------------------------------------------------------------- /src/session.h: -------------------------------------------------------------------------------- 1 | // ndppd - NDP Proxy Daemon 2 | // Copyright (C) 2011 Daniel Adolfsson 3 | // 4 | // This program is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program. If not, see . 16 | #pragma once 17 | 18 | #include 19 | #include 20 | 21 | #include "ndppd.h" 22 | 23 | NDPPD_NS_BEGIN 24 | 25 | class proxy; 26 | class iface; 27 | 28 | class session { 29 | private: 30 | weak_ptr _ptr; 31 | 32 | weak_ptr _pr; 33 | 34 | address _saddr, _daddr, _taddr; 35 | 36 | bool _autowire; 37 | 38 | bool _keepalive; 39 | 40 | bool _wired; 41 | 42 | address _wired_via; 43 | 44 | bool _touched; 45 | 46 | // An array of interfaces this session is monitoring for 47 | // ND_NEIGHBOR_ADVERT on. 48 | std::list > _ifaces; 49 | 50 | std::list > _pending; 51 | 52 | // The remaining time in miliseconds the object will stay in the 53 | // interface's session array or cache. 54 | int _ttl; 55 | 56 | int _fails; 57 | 58 | int _retries; 59 | 60 | int _status; 61 | 62 | static std::list > _sessions; 63 | 64 | public: 65 | enum 66 | { 67 | WAITING, // Waiting for an advert response. 68 | RENEWING, // Renewing; 69 | VALID, // Valid; 70 | INVALID // Invalid; 71 | }; 72 | 73 | static void update_all(int elapsed_time); 74 | 75 | // Destructor. 76 | ~session(); 77 | 78 | static ptr create(const ptr& pr, const address& taddr, bool autowire, bool keepalive, int retries); 79 | 80 | void add_iface(const ptr& ifa); 81 | 82 | void add_pending(const address& addr); 83 | 84 | const address& taddr() const; 85 | 86 | const address& daddr() const; 87 | 88 | const address& saddr() const; 89 | 90 | bool autowire() const; 91 | 92 | int retries() const; 93 | 94 | int fails() const; 95 | 96 | bool keepalive() const; 97 | 98 | bool wired() const; 99 | 100 | bool touched() const; 101 | 102 | int status() const; 103 | 104 | void status(int val); 105 | 106 | void handle_advert(); 107 | 108 | void handle_advert(const address& saddr, const std::string& ifname, bool use_via); 109 | 110 | void handle_auto_wire(const address& saddr, const std::string& ifname, bool use_via); 111 | 112 | void handle_auto_unwire(const std::string& ifname); 113 | 114 | void touch(); 115 | 116 | void send_advert(const address& daddr); 117 | 118 | void send_solicit(); 119 | 120 | void refesh(); 121 | }; 122 | 123 | NDPPD_NS_END 124 | --------------------------------------------------------------------------------