├── COPYING ├── README.md ├── example ├── multi.yaml └── multi_netlinkrecv.c └── src ├── CMakeLists.txt ├── Makefile ├── files ├── multi.init ├── multi.yml ├── systemd │ ├── conffiles │ ├── multi.service │ ├── postinst │ └── prerm └── upstart │ ├── multi.conf │ ├── postinst │ └── prerm ├── multi_cmp.c ├── multi_cmp.h ├── multi_common.h ├── multi_core.c ├── multi_core.h ├── multi_dhcp_common.h ├── multi_dhcp_constants.h ├── multi_dhcp_main.c ├── multi_dhcp_main.h ├── multi_dhcp_network.c ├── multi_dhcp_network.h ├── multi_dhcp_protocol.c ├── multi_dhcp_protocol.h ├── multi_link_core.c ├── multi_link_core.h ├── multi_link_filter.c ├── multi_link_filter.h ├── multi_link_netlink.c ├── multi_link_netlink.h ├── multi_link_shared.h ├── multi_macros.h ├── multi_multicast.c └── multi_shared.h /COPYING: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MULTI is deprecated. Please see [here](https://github.com/kristrev/multihomed-routing) for a better approach which works with ifupdown and NetworkManager 2 | 3 | MULTI Network Manager 4 | ===================== 5 | 6 | MULTI Network Manager (MNM) is a command line network manager for Linux, with 7 | proper support for multihoming (currently IPv4 only). It automatically detects 8 | new network interfaces, acquires an IP (using for example DHCP or read from 9 | config file) and configures the routing table(s) accordingly, using 10 | Netlink/RTNetlink-messages. MNM supports the following command line options: 11 | 12 | * -d : Run MNM as daemon. Logs messages to the log file locatin (see below). 13 | * -l : Log file location, defaults to /var/log/multi.log 14 | * -u : Ensure that each interface is assigned a unique IP. Use this one with 15 | care, as DHCP has no way to gracefully reject an IP address. Instead, DHCP 16 | DECLINE is used, which causes DHCP servers to blacklist IP addresses. 17 | * -c filename : Provide a configuration file allowing you to specify which 18 | interfaces shall be assigned a static IP (and the IP of course), as well as 19 | static metrics. An example configuration file is in the /examples-directory. 20 | 21 | Features 22 | -------- 23 | * Automatically detects new network interfaces and configures the routing 24 | subsystem accordingly. 25 | * Proper multihoming support, the routing configuration is done so that all 26 | interfaces can be used (unlike the default Linux/ip-behavior). 27 | * Supports specifying static IPs and metrics, using an easy configuration file 28 | format. 29 | * Broadcasts information about network events. This enables applications to 30 | easily adapt to changes in network state. The file multi\_netlinkrecv.c in 31 | /examples shows how this information can be read. 32 | 33 | How to install 34 | -------------- 35 | First, install the required dependencies. These are libiw, glib, libyaml and 36 | libmnl. If you are using Ubuntu or Debian, the package names are libiw-dev, 37 | libglib2.0-dev, libyaml-dev and libmnl-dev. Then, either use cmake or normal 38 | make directly to compile MNM. 39 | 40 | Note that the library versions referenced in the CMake-file are those I have used 41 | when working on MNM lately. Please let me know if it MNM compiles and works with 42 | older versions, and I will update the CMake-file. 43 | 44 | Notes 45 | ----- 46 | For MNM to work properly, other network managers (for example GNOME's) must be 47 | disabled, or interfaces have to be configured with a static IP (then MNM will configure properly). Also, make sure that no dhclient instances are running on any of the 48 | interfaces you want to configure. In order to set interfaces to automatically 49 | come up, but don't get an IP (i.e., for use with MNM), set them to manual in 50 | /etc/network/interfaces. For example: 51 | 52 | iface eth0 inet manual 53 | up ifconfig $IFACE 0.0.0.0 up 54 | down ifconfig $IFACE down 55 | 56 | If you are going to use MNM with PPP devices, make sure the 'nodefaultroute' 57 | option is specified. MNM will create this route automatically. 58 | 59 | Unless a static IP is specified, MNM assumes that IPs for LAN and WLAN 60 | interfaces will be obtained using DHCP. For PPP devices, MNM assumes that an IP 61 | has been allocated by the ISP and set by the dialer. 62 | 63 | Future work 64 | ----------- 65 | * IPv6 support: Due to a lack of available IPv6 networks, I have not been able 66 | to add IPv6 support to MNM. This is on my schedule, but if anyone wants to 67 | contribute, that would be great. 68 | * Improved error handling: Currently, the user is not notified when for example 69 | he or she provides an incorrect route. This is due to some tricky message 70 | handling that I have not decided on how to deal with yet. Some 71 | RTNETLINK-messages generate replies, while others don't. 72 | * General code clean-up: The application has proven to be stable (used in a scientific 73 | test network for over a year now) and without memory leaks, but the overall 74 | structure still bears the marks of my (then) inexperience with netlink and more advanced 75 | event loop designs. 76 | 77 | Motivation 78 | ---------- 79 | Linux supports multihoming, however, when a Linux-device is connected to 80 | multiple networks simultaneously, the kernel will often be unable to make a 81 | routing decision due to overlapping routes. 82 | 83 | The most common technique for configuring the routing subsystem in the presence 84 | of multiple active interfaces, is to use scripts. This is a static and error 85 | prone process, that does not scale. For example, scripts and route metrics needs 86 | to be updated as new interfaces are added. 87 | 88 | While working on my PhD, I had to make several experiments on multihomed hosts 89 | with different number of active network interfaces. After I had made one to many 90 | configuration mistakes with my setup scripts, I decided to write MNM. 91 | 92 | Contact 93 | ------- 94 | 95 | If you have any questions, comments or just want to say hi, please contact me at 96 | kristian.evensen@gmail.com. 97 | -------------------------------------------------------------------------------- /example/multi.yaml: -------------------------------------------------------------------------------- 1 | #Each entry has to start with the interface name, newline and then the options 2 | #Valid options are: 3 | #- (address, netmask, gateway): Only address and netmask is required 4 | #- metric: desired metric for interface (must be between 1 and 32) 5 | #- proto: static, other or ignore. other means that multi assumes how to get IP, 6 | #ignore means that multi will ignore the interface. 7 | 8 | eth0: 9 | proto: 'other' 10 | # address: 10.110.111.2 11 | # netmask: 255.255.255.0 12 | # gateway: 10.110.111.1 13 | metric: 32 14 | 15 | wwan0: 16 | proto: 'static' 17 | address: 10.110.111.2 18 | netmask: 255.255.255.0 19 | gateway: 10.110.111.1 20 | 21 | ppp0: 22 | proto: 'other' 23 | metric: 1 24 | 25 | ppp1: 26 | proto: 'other' 27 | metric: 2 28 | 29 | ppp2: 30 | proto: 'other' 31 | metric: 3 32 | 33 | ppp3: 34 | proto: 'other' 35 | metric: 4 36 | -------------------------------------------------------------------------------- /example/multi_netlinkrecv.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | typedef enum{ 10 | LINK_DOWN = 0, 11 | LINK_UP 12 | } link_state; 13 | 14 | #define MAX_BUFSIZE 1500 15 | 16 | int main(int argc, char *argv[]){ 17 | struct sockaddr_nl src_addr, dest_addr; 18 | struct nlmsghdr *nlh = NULL; 19 | struct iovec iov; 20 | struct msghdr msg; 21 | int32_t sockfd, retval; 22 | uint8_t *buf; 23 | uint8_t devname[IFNAMSIZ]; 24 | int32_t *ifi_idx = NULL; 25 | 26 | if((sockfd = socket(PF_NETLINK, SOCK_RAW, NETLINK_GENERIC)) < 0){ 27 | perror("Could not create netlink socket"); 28 | exit(EXIT_FAILURE); 29 | } 30 | 31 | memset(&src_addr, 0, sizeof(src_addr)); 32 | memset(&dest_addr, 0, sizeof(dest_addr)); 33 | 34 | src_addr.nl_family = AF_NETLINK; 35 | src_addr.nl_pid = getpid(); 36 | src_addr.nl_groups = 1; 37 | 38 | if(bind(sockfd, (struct sockaddr*) &src_addr, sizeof(src_addr)) < 0){ 39 | perror("Could not bind netlink socket"); 40 | exit(EXIT_FAILURE); 41 | } 42 | 43 | nlh = (struct nlmsghdr *) malloc(NLMSG_SPACE(MAX_BUFSIZE)); 44 | memset(nlh, 0, NLMSG_SPACE(MAX_BUFSIZE)); 45 | 46 | iov.iov_base = (void*) nlh; 47 | iov.iov_len = NLMSG_SPACE(MAX_BUFSIZE); 48 | msg.msg_name = (void *) &dest_addr; 49 | msg.msg_namelen = sizeof(dest_addr); 50 | msg.msg_iov = &iov; 51 | msg.msg_iovlen = 1; 52 | 53 | fprintf(stderr, "Ready to receive netlink multicast messages on socket %d\n", sockfd); 54 | 55 | while(1){ 56 | retval = recvmsg(sockfd, &msg, 0); 57 | buf = NLMSG_DATA(nlh); 58 | 59 | ifi_idx = (uint32_t *) (buf+1); 60 | fprintf(stderr, "Index %u State %u %d\n", *ifi_idx, buf[0], retval); 61 | 62 | if(if_indextoname(*ifi_idx, devname) == NULL){ 63 | printf("APP: Could not find interface name for index %u\n", *ifi_idx); 64 | memcpy(devname, "NULL", 5); 65 | } 66 | 67 | if(buf[0] == LINK_UP) 68 | printf("APP: Interface %s is up, index %u\n", devname, *ifi_idx); 69 | else 70 | printf("APP: Interface %s is down (can be wrong, not to be trusted), index %u\n", devname, buf[1]); 71 | } 72 | 73 | fprintf(stderr, "Received %d bytes\n", retval); 74 | } 75 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include(FindPkgConfig) 2 | 3 | #Standard stuff 4 | cmake_minimum_required(VERSION 2.6) 5 | project(multi-client) 6 | 7 | find_package(Threads) 8 | 9 | set(VALID_INIT_DAEMONS upstart systemd) 10 | 11 | if (NOT DEFINED INIT_DAEMON) 12 | set(INIT_DAEMON "upstart") 13 | message(STATUS "INIT_DAEMON is not defined, select upstart by default") 14 | else() 15 | set(VALID 0) 16 | foreach(daemon ${VALID_INIT_DAEMONS}) 17 | if (INIT_DAEMON STREQUAL ${daemon}) 18 | set(VALID 1) 19 | break() 20 | endif() 21 | endforeach() 22 | 23 | if (NOT ${VALID}) 24 | message(STATUS "INIT_DAEMON defined in cmdline is not valid, select upstart by default") 25 | set(INIT_DAEMON "upstart") 26 | endif() 27 | endif() 28 | 29 | message(STATUS "INIT_DAEMON is set to " ${INIT_DAEMON}) 30 | 31 | #Library check 32 | pkg_check_modules(LIBMNL REQUIRED libmnl>=1.0.1-1) 33 | pkg_check_modules(LIBYAML REQUIRED yaml-0.1>=0.1.4) 34 | 35 | #Static way of locating library 36 | find_library(LIBIW_LIBRARY iw) 37 | 38 | if(NOT LIBIW_LIBRARY) 39 | message(FATAL_ERROR "Libiw could not be found") 40 | endif(NOT LIBIW_LIBRARY) 41 | 42 | set(CMAKE_C_FLAGS "-g -O") 43 | 44 | #Debian package generator 45 | set(CPACK_GENERATOR "DEB") 46 | set(CPACK_PACKAGE_VERSION_MAJOR "0") 47 | set(CPACK_PACKAGE_VERSION_MINOR "1") 48 | set(CPACK_PACKAGE_VERSION_PATCH "0") 49 | set(CPACK_DEBIAN_PACKAGE_MAINTAINER "Kristian R. Evensen ") 50 | set(CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA 51 | "${CMAKE_CURRENT_SOURCE_DIR}/files/${INIT_DAEMON}/postinst;${CMAKE_CURRENT_SOURCE_DIR}/files/${INIT_DAEMON}/prerm;${CMAKE_CURRENT_SOURCE_DIR}/files/${INIT_DAEMON}/conffiles") 52 | set (CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON) 53 | include(CPack) 54 | 55 | add_executable(multi_client multi_multicast.c multi_core.c multi_dhcp_main.c 56 | multi_dhcp_network.c multi_dhcp_protocol.c multi_link_core.c 57 | multi_link_filter.c multi_link_netlink.c multi_common.h multi_core.h 58 | multi_dhcp_common.h multi_dhcp_constants.h multi_dhcp_main.h 59 | multi_dhcp_network.h multi_dhcp_protocol.h multi_link_core.h 60 | multi_link_filter.h multi_link_netlink.h multi_link_shared.h multi_shared.h 61 | multi_macros.h multi_cmp.h multi_cmp.c) 62 | target_link_libraries(multi_client ${LIBMNL_LIBRARIES}) 63 | target_link_libraries(multi_client ${LIBYAML_LIBRARIES}) 64 | target_link_libraries(multi_client ${LIBIW_LIBRARY}) 65 | target_link_libraries(multi_client ${CMAKE_THREAD_LIBS_INIT}) 66 | install(TARGETS multi_client RUNTIME DESTINATION /usr/local/sbin) 67 | 68 | if (INIT_DAEMON STREQUAL "systemd") 69 | install(FILES ${PROJECT_SOURCE_DIR}/files/systemd/multi.service DESTINATION 70 | /lib/systemd/system/ PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ 71 | RENAME multi.service) 72 | install(FILES ${PROJECT_SOURCE_DIR}/files/multi.yml DESTINATION 73 | /etc/ PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ) 74 | elseif(INIT_DAEMON STREQUAL "upstart") 75 | install(FILES ${PROJECT_SOURCE_DIR}/files/upstart/multi.conf DESTINATION 76 | /etc/init/ PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ 77 | RENAME multi.conf) 78 | endif() 79 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | CFLAGS = `pkg-config --cflags glib-2.0` -g 3 | LIBS = -l pthread -l iw -l yaml `pkg-config --libs glib-2.0 libmnl` 4 | SRC_FILES = *.c 5 | 6 | all: multi_client 7 | 8 | multi_client: $(SRC_FILES) 9 | $(CC) $(CFLAGS) -o $@ $^ $(LIBS) 10 | 11 | clean: 12 | rm -f multi_client *.o 13 | 14 | .PHONY: clean 15 | -------------------------------------------------------------------------------- /src/files/multi.init: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | #Remember that facilities beginning with a $ is provided by the system 4 | #local_fs is required because multi writes to /var/ (according to LSB 3.1) 5 | ### BEGIN INIT INFO 6 | # Provides: multi 7 | # Required-Start: $network $local_fs 8 | # Required-Stop: $network $local_fs 9 | # Default-Start: 2 3 4 5 10 | # Default-Stop: 0 1 6 11 | # Short-Description: MULTI multihomed network manager 12 | # Description: MULTI is a multihomed-compatible network manager, which also 13 | # broadcasts NW information 14 | ### END INIT INFO 15 | MULTI_BIN=/usr/sbin/multi_client 16 | 17 | case "$1" in 18 | start) 19 | echo "Starting MULTI network manager as daemon ..." #Expand variable 20 | if start-stop-daemon --oknodo --exec ${MULTI_BIN} --start -- -d; then #Start-deamen is fantastic 21 | echo "MULTI started sucessfully." 22 | else 23 | echo "MULTI failed to start." 24 | fi 25 | ;; 26 | stop) 27 | echo "Stopping MULTI network manager ..." 28 | if start-stop-daemon --oknodo --exec ${MULTI_BIN} --stop --retry 2; then 29 | echo "MULTI was stopped." 30 | else 31 | echo "MULTI could not be stopped sucessfully." 32 | fi 33 | ;; 34 | restart|force-reload) 35 | #Put these as functions 36 | echo "Stopping MULTI network manager ..." 37 | if start-stop-daemon --oknodo --exec ${MULTI_BIN} --stop --retry 2; then 38 | echo "MULTI was stopped." 39 | else 40 | echo "MULTI could not be stopped sucessfully." 41 | exit 1 42 | fi 43 | 44 | sleep 5 45 | 46 | echo "Starting MULTI network manager as daemon ..." #Expand variable 47 | if start-stop-daemon --oknodo --exec ${MULTI_BIN} --start -- -d; then #Start-deamen is fantastic 48 | echo "MULTI started sucessfully." 49 | else 50 | echo "MULTI failed to start." 51 | exit 1 52 | fi 53 | 54 | ;; 55 | status) 56 | start-stop-daemon --oknodo --exec ${MULTI_BIN} --status 57 | exit $? 58 | ;; 59 | *) 60 | echo "Usage /etc/init.d/multi {start|stop|restart|status|force-reload}" 61 | exit 1 62 | ;; 63 | esac 64 | 65 | exit 0 66 | -------------------------------------------------------------------------------- /src/files/multi.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kristrev/multi/14a2a143e11065eb0f4171060ab25c6347f52004/src/files/multi.yml -------------------------------------------------------------------------------- /src/files/systemd/conffiles: -------------------------------------------------------------------------------- 1 | /etc/multi.yml 2 | -------------------------------------------------------------------------------- /src/files/systemd/multi.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Multi network manager 3 | After=network.target 4 | 5 | [Service] 6 | ExecStart=/usr/local/sbin/multi_client -d -c /etc/multi.yml 7 | Type=forking 8 | Restart=on-failure 9 | 10 | # 'sshd -D' leaks stderr and confuses things in conjunction with 'console log' 11 | StandardError=null 12 | StandardOutput=null 13 | 14 | [Install] 15 | WantedBy=multi-user.target 16 | -------------------------------------------------------------------------------- /src/files/systemd/postinst: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | #/usr/sbin/update-rc.d multi defaults 4 | #/etc/init.d/multi restart 5 | 6 | systemctl enable multi 7 | systemctl start multi || true 8 | -------------------------------------------------------------------------------- /src/files/systemd/prerm: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | systemctl stop multi 4 | systemctl disable multi 5 | -------------------------------------------------------------------------------- /src/files/upstart/multi.conf: -------------------------------------------------------------------------------- 1 | description "Multi network manager" 2 | 3 | start on networking 4 | stop on runlevel [!2345] 5 | 6 | respawn 7 | respawn limit unlimited 8 | 9 | # 'sshd -D' leaks stderr and confuses things in conjunction with 'console log' 10 | console none 11 | 12 | exec /usr/local/sbin/multi_client 13 | -------------------------------------------------------------------------------- /src/files/upstart/postinst: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | #/usr/sbin/update-rc.d multi defaults 4 | #/etc/init.d/multi restart 5 | 6 | initctl reload-configuration 7 | initctl start multi 8 | -------------------------------------------------------------------------------- /src/files/upstart/prerm: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | initctl stop multi 4 | initctl reload-configuration 5 | -------------------------------------------------------------------------------- /src/multi_cmp.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "multi_cmp.h" 4 | #include "multi_link_shared.h" 5 | #include "multi_shared.h" 6 | 7 | //TODO: Look into merging some of the cmp methods 8 | uint8_t multi_cmp_devname(void *a, void *b){ 9 | struct multi_link_info_static *li = (struct multi_link_info_static *) a; 10 | char *dev_name = (char *) b; 11 | 12 | if(!strcmp((char*) li->dev_name, (char*) dev_name)) 13 | return 0; 14 | else 15 | return 1; 16 | } 17 | 18 | uint8_t multi_cmp_ifidx(void *a, void *b){ 19 | struct multi_link_info *li = (struct multi_link_info *) a; 20 | uint32_t *ifiIdx = (uint32_t*) b; 21 | 22 | if(li->ifi_idx == *ifiIdx) 23 | return 0; 24 | else 25 | return 1; 26 | } 27 | 28 | /* This function needs to be separate. It is used when flushing infromation */ 29 | uint8_t multi_cmp_ifidx_flush(void *a, void *b){ 30 | struct multi_link_info *li = (struct multi_link_info *) a; 31 | struct ifaddrmsg *ifa = (struct ifaddrmsg *) b; 32 | 33 | //Ignore PPP interfaces, as they will not be flushed! 34 | if((li->state != GOT_IP_PPP && li->state != GOT_IP_AP) && li->ifi_idx == 35 | ifa->ifa_index) 36 | return 0; 37 | else 38 | return 1; 39 | } 40 | 41 | -------------------------------------------------------------------------------- /src/multi_cmp.h: -------------------------------------------------------------------------------- 1 | #ifndef MULTI_CMP_H 2 | #define MULTI_CMP_H 3 | 4 | #include 5 | 6 | uint8_t multi_cmp_devname(void *a, void *b); 7 | uint8_t multi_cmp_ifidx(void *a, void *b); 8 | uint8_t multi_cmp_ifidx_flush(void *a, void *b); 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /src/multi_common.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Kristian Evensen 3 | * 4 | * This file is part of Multi Network Manager (MNM). MNM is free software: you 5 | * can redistribute it and/or modify it under the terms of the Lesser GNU 6 | * General Public License as published by the Free Software Foundation, either 7 | * version 3 of the License, or (at your option) any later version. 8 | * 9 | * MNM is distributed in the hope that it will be useful, but WITHOUT ANY 10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License along with 14 | * Network Device Listener. If not, see http://www.gnu.org/licenses/. 15 | */ 16 | 17 | #ifndef MULTI_COMMON_H 18 | #define MULTI_COMMON_H 19 | 20 | #include 21 | 22 | #define MAX_BUFSIZE 1500 23 | 24 | typedef enum{ 25 | //Initial state, link cannot be used (can still be up and running) 26 | LINK_DOWN=0, 27 | //Link is ready and can be used 28 | LINK_UP, 29 | //Signal that information about the PPP interface must be collected. Needed to restore state, because of non-working nested wilddump/filter 30 | LINK_DOWN_PPP, 31 | //Signal that information about a wireless ap interface must be collected 32 | LINK_DOWN_AP, 33 | //Link has lost its current IP address and cant be used 34 | LINK_INVALID, 35 | //If DHCP is to be used on this link, indicates that link is waiting for DHCP to finish 36 | WAITING_FOR_DHCP, 37 | //DHCP has finished successfully, info store in cfg and the main thread will configure interface 38 | GOT_IP_DHCP, 39 | //Static IP, interface is up, but not running (so no cable attached) 40 | GOT_IP_STATIC_UP, 41 | //Has a static IP address 42 | GOT_IP_STATIC, 43 | //This is a PPP interface, which will be given an IP automaticually 44 | GOT_IP_PPP, 45 | //Got the information about the wireless access point interface 46 | GOT_IP_AP, 47 | //DHCP failed, the application will ignore this interface forever (for now). 48 | //TODO: Decide if it should be freed. 49 | DHCP_FAILED, 50 | //DHCP got a new IP after a RENEW/REBIND, must first flush then add new info 51 | DHCP_IP_CHANGED, 52 | //DHCP lease has expired and a new IP has not been received. This does not 53 | //mean that the interface is down, but it cant be used! 54 | DHCP_IP_INVALID, 55 | //Interface is up, but not active (i.e., no cable) 56 | LINK_UP_STATIC_IFF, 57 | //Link is up and with a static IP (used to avoid seg fault when link goes 58 | //down!) 59 | LINK_UP_STATIC, 60 | //Same as above, but for PPP 61 | LINK_UP_PPP, 62 | //Access point is up and configured 63 | LINK_UP_AP, 64 | //Interface has previously been allocated an IP, try to reuse that one 65 | REBOOT_DHCP, 66 | //The link module has marked this link for deletion (needed because 67 | //g_slist_foreach is not safe) 68 | DELETE_LINK 69 | } link_state; 70 | 71 | #define MULTI_LOG_PREFIX "[%.2d:%.2d:%.2d %.2d/%.2d/%d]: " 72 | #define MULTI_DEBUG_PRINT2(fd, ...){fprintf(fd, __VA_ARGS__);fflush(fd);} 73 | #define MULTI_DEBUG_SYSLOG(priority, ...){syslog(LOG_MAKEPRI(LOG_DAEMON, priority), __VA_ARGS__);} 74 | //The ## is there so that I dont have to fake an argument when I use the macro 75 | //on string without arguments! 76 | #define MULTI_DEBUG_PRINT(fd, _fmt, ...) \ 77 | do { \ 78 | time_t rawtime; \ 79 | struct tm *curtime; \ 80 | time(&rawtime); \ 81 | curtime = gmtime(&rawtime); \ 82 | MULTI_DEBUG_PRINT2(fd, MULTI_LOG_PREFIX _fmt, curtime->tm_hour, \ 83 | curtime->tm_min, curtime->tm_sec, curtime->tm_mday, \ 84 | curtime->tm_mon + 1, 1900 + curtime->tm_year, \ 85 | ##__VA_ARGS__);} while(0) 86 | 87 | #define MULTI_DEBUG_PRINT_SYSLOG(fd, _fmt, ...) \ 88 | do { \ 89 | time_t rawtime; \ 90 | struct tm *curtime; \ 91 | time(&rawtime); \ 92 | curtime = gmtime(&rawtime); \ 93 | if (1) \ 94 | MULTI_DEBUG_SYSLOG(LOG_INFO, _fmt, ##__VA_ARGS__); \ 95 | MULTI_DEBUG_PRINT2(fd, MULTI_LOG_PREFIX _fmt, \ 96 | curtime->tm_hour, \ 97 | curtime->tm_min, curtime->tm_sec, curtime->tm_mday, \ 98 | curtime->tm_mon + 1, 1900 + curtime->tm_year, \ 99 | ##__VA_ARGS__);} while(0) 100 | #endif 101 | -------------------------------------------------------------------------------- /src/multi_core.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Kristian Evensen 3 | * 4 | * This file is part of Multi Network Manager (MNM). MNM is free software: you 5 | * can redistribute it and/or modify it under the terms of the Lesser GNU 6 | * General Public License as published by the Free Software Foundation, either 7 | * version 3 of the License, or (at your option) any later version. 8 | * 9 | * MNM is distributed in the hope that it will be useful, but WITHOUT ANY 10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License along with 14 | * Network Device Listener. If not, see http://www.gnu.org/licenses/. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include "multi_core.h" 27 | #include "multi_link_core.h" 28 | #include "multi_shared.h" 29 | #include "multi_common.h" 30 | 31 | extern void* multi_link_module_init(void *arg); 32 | extern void* multi_probe_module_init(void *arg); 33 | 34 | static uint8_t multi_core_store_address(struct multi_link_info_static *mlis, 35 | uint8_t *key_data, uint8_t *value_data, uint8_t *addr_count){ 36 | struct in_addr ipaddr; 37 | 38 | if(inet_pton(AF_INET, value_data, &ipaddr) == 0){ 39 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Could not convert %s (invalid parameter?)\n", 40 | value_data); 41 | return 1; 42 | } 43 | 44 | //Only netmask and address is required, if any address information is 45 | //specified 46 | if(!strcmp(key_data, ADDRESS)){ 47 | memcpy(&(mlis->cfg_static.address), &ipaddr, sizeof(struct in_addr)); 48 | (*addr_count)++; 49 | } else if(!strcmp(key_data, NETMASK)){ 50 | memcpy(&(mlis->cfg_static.netmask), &ipaddr, sizeof(struct in_addr)); 51 | (*addr_count)++; 52 | } else if(!strcmp(key_data, GATEWAY)) 53 | memcpy(&(mlis->cfg_static.gateway), &ipaddr, sizeof(struct in_addr)); 54 | else { 55 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Found unknown parameter %s\n", key_data); 56 | return 1; 57 | } 58 | 59 | return 0; 60 | } 61 | 62 | uint8_t multi_core_parse_iface_info(struct multi_link_info_static *mlis, 63 | yaml_parser_t *parser){ 64 | yaml_event_t key, value; 65 | uint8_t *value_data, *key_data; 66 | uint8_t error = 0; 67 | uint32_t metric = 0; 68 | 69 | //Used for checking if address, netmask and gateway is provided (all 70 | //required if one is present) 71 | uint8_t addr_count = 0; 72 | //Proto must be set 73 | uint8_t proto = 0; 74 | 75 | while(1){ 76 | if(!yaml_parser_parse(parser, &key)){ 77 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Could not parse key\n"); 78 | error = 1; 79 | break; 80 | } 81 | 82 | if(key.type == YAML_MAPPING_END_EVENT){ 83 | yaml_event_delete(&key); 84 | if((addr_count > 0 && addr_count != 2) || !proto){ 85 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Required information" 86 | "(address/netmask/proto) is missing\n"); 87 | error = 1; 88 | } 89 | 90 | break; 91 | } else if(key.type == YAML_SCALAR_EVENT){ 92 | if(!yaml_parser_parse(parser, &value)){ 93 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Could not parse value\n"); 94 | error = 1; 95 | break; 96 | } else if(value.type != YAML_SCALAR_EVENT){ 97 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Found something else than scalar\n"); 98 | yaml_event_delete(&value); 99 | error = 1; 100 | break; 101 | } 102 | 103 | key_data = key.data.scalar.value; 104 | value_data = value.data.scalar.value; 105 | 106 | if(!strcmp(key_data, METRIC)){ 107 | metric = atoi(value_data); 108 | //Metric of 0 is not allowed 109 | if(!metric || metric > MAX_NUM_LINKS){ 110 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Invalid metric\n"); 111 | error = 1; 112 | break; 113 | } else { 114 | //Start at index 0 115 | mlis->metric = metric; 116 | 117 | //Check if metric is set 118 | if(multi_shared_metrics_set & (1<<(metric-1))){ 119 | MULTI_DEBUG_PRINT_SYSLOG(stderr, 120 | "Metric for %s is already used\n", 121 | mlis->dev_name); 122 | error = 1; 123 | break; 124 | } 125 | 126 | //The static metrics are always reserved 127 | multi_shared_metrics_set ^= 1 << (metric-1); 128 | } 129 | } else if(!strcmp(key_data, PROTO)){ 130 | proto = 1; 131 | 132 | if(!strcmp(value_data, "static")) 133 | mlis->proto = PROTO_STATIC; 134 | else if(!strcmp(value_data, "other")) 135 | mlis->proto = PROTO_OTHER; 136 | else if(!strcmp(value_data, "ignore")) 137 | mlis->proto = PROTO_IGNORE; 138 | else{ 139 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Unknown protocol\n"); 140 | error = 1; 141 | break; 142 | } 143 | } else { 144 | if((error = multi_core_store_address(mlis, key_data, value_data, 145 | &addr_count))) 146 | break; 147 | } 148 | 149 | DEL_KEY_VALUE(key, value); 150 | } 151 | } 152 | 153 | return error; 154 | } 155 | 156 | static uint8_t multi_core_parse_config(uint8_t *cfg_filename){ 157 | yaml_parser_t parser; 158 | yaml_event_t event; 159 | FILE *cfgfile = NULL; 160 | uint8_t error = 0; 161 | struct multi_link_info_static *mlis; 162 | 163 | //Only in use when a configuration file is present 164 | TAILQ_INIT(&multi_shared_static_links); 165 | 166 | if((cfgfile = fopen(cfg_filename, "rb")) == NULL){ 167 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Could not open configuration file\n"); 168 | error = 1; 169 | return error; 170 | } 171 | 172 | //Initialized the parser 173 | assert(yaml_parser_initialize(&parser)); 174 | yaml_parser_set_input_file(&parser, cfgfile); 175 | 176 | while(1){ 177 | if(!yaml_parser_parse(&parser, &event)){ 178 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Parsing failed\n"); 179 | error = 1; 180 | break; 181 | } 182 | 183 | if(event.type == YAML_STREAM_END_EVENT){ 184 | //LibYAML might allocate memory for events and so forth. Must 185 | //therefore free 186 | yaml_event_delete(&event); 187 | break; 188 | } else if(event.type == YAML_SCALAR_EVENT){ 189 | if(strlen(event.data.scalar.value) > IFNAMSIZ){ 190 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Interface name is too long\n"); 191 | error = 1; 192 | break; 193 | } 194 | 195 | //The outer loop should only see scalars, which is the interface 196 | //names 197 | mlis = (struct multi_link_info_static *) 198 | malloc(sizeof(struct multi_link_info_static)); 199 | memset(mlis, 0, sizeof(*mlis)); 200 | mlis->metric = 0; 201 | memcpy(mlis->dev_name, event.data.scalar.value, 202 | strlen(event.data.scalar.value) + 1); 203 | yaml_event_delete(&event); 204 | 205 | //Make sure next event is mapping! 206 | if(!yaml_parser_parse(&parser, &event)){ 207 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Parsing failed\n"); 208 | error = 1; 209 | break; 210 | } else if(event.type != YAML_MAPPING_START_EVENT){ 211 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Configuration file is incorrect." 212 | "No information for interface\n"); 213 | error = 1; 214 | break; 215 | } 216 | 217 | if(multi_core_parse_iface_info(mlis, &parser)){ 218 | MULTI_DEBUG_PRINT_SYSLOG(stderr, 219 | "Parsing of configuration file failed\n"); 220 | error = 1; 221 | break; 222 | } else { 223 | TAILQ_INSERT_TAIL(&multi_shared_static_links, mlis, 224 | list_ptr); 225 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Interface %s added to static list\n", 226 | mlis->dev_name); 227 | } 228 | } 229 | } 230 | 231 | yaml_parser_delete(&parser); 232 | fclose(cfgfile); 233 | 234 | return error; 235 | } 236 | 237 | /* Allocates and returns a config struct needed for multi to work */ 238 | struct multi_config* multi_core_initialize_config(uint8_t *cfg_file, 239 | uint8_t unique){ 240 | struct multi_config *mc; 241 | 242 | multi_shared_metrics_set = 0; 243 | //multi_shared_metrics_set = 0; 244 | mc = (struct multi_config *) malloc(sizeof(struct multi_config)); 245 | memset(mc, 0, sizeof(struct multi_config)); 246 | 247 | /* Not mandatory */ 248 | if(cfg_file != NULL) 249 | //Parse configuration file 250 | if(multi_core_parse_config(cfg_file)) 251 | return NULL; 252 | 253 | 254 | if(pipe(mc->socket_pipe) == -1){ 255 | MULTI_DEBUG_PRINT_SYSLOG(stderr,"Failed to create pipe\n"); 256 | return NULL; 257 | } 258 | 259 | //Require unique IP address or not 260 | mc->unique = unique; 261 | 262 | return mc; 263 | } 264 | 265 | /* This will also be started as a thread */ 266 | static void* multi_core_init(void *arg){ 267 | struct multi_core_sync mcs; 268 | pthread_t link_thread, probing_thread; 269 | struct multi_core_sync *mcs_main = (struct multi_core_sync *) arg; 270 | 271 | mcs.mc = mcs_main->mc; 272 | pthread_cond_init(&(mcs.sync_cond), NULL); 273 | pthread_mutex_init(&(mcs.sync_mutex), NULL); 274 | 275 | /* Needs to be served config file, but ignore that for now */ 276 | pthread_mutex_lock(&(mcs.sync_mutex)); 277 | pthread_create(&link_thread, NULL, multi_link_module_init, &mcs); 278 | pthread_cond_wait(&(mcs.sync_cond), &(mcs.sync_mutex)); 279 | pthread_mutex_unlock(&(mcs.sync_mutex)); 280 | 281 | MULTI_DEBUG_PRINT_SYSLOG(stderr,"MULTI is ready and running\n"); 282 | 283 | pthread_mutex_lock(&(mcs_main->sync_mutex)); 284 | pthread_cond_signal(&(mcs_main->sync_cond)); 285 | pthread_mutex_unlock(&(mcs_main->sync_mutex)); 286 | 287 | pthread_join(link_thread, NULL); 288 | 289 | return NULL; 290 | } 291 | 292 | /* Starts the multi thread */ 293 | pthread_t multi_start(struct multi_config *mc){ 294 | pthread_t multi_thread; 295 | struct multi_core_sync mcs; 296 | 297 | mcs.mc = mc; 298 | pthread_cond_init(&(mcs.sync_cond), NULL); 299 | pthread_mutex_init(&(mcs.sync_mutex), NULL); 300 | 301 | /* Start MULTI */ 302 | pthread_mutex_lock(&(mcs.sync_mutex)); 303 | pthread_create(&multi_thread, NULL, multi_core_init, &mcs); 304 | pthread_cond_wait(&(mcs.sync_cond), &(mcs.sync_mutex)); 305 | pthread_mutex_unlock(&(mcs.sync_mutex)); 306 | 307 | return multi_thread; 308 | } 309 | -------------------------------------------------------------------------------- /src/multi_core.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Kristian Evensen 3 | * 4 | * This file is part of Multi Network Manager (MNM). MNM is free software: you 5 | * can redistribute it and/or modify it under the terms of the Lesser GNU 6 | * General Public License as published by the Free Software Foundation, either 7 | * version 3 of the License, or (at your option) any later version. 8 | * 9 | * MNM is distributed in the hope that it will be useful, but WITHOUT ANY 10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License along with 14 | * Network Device Listener. If not, see http://www.gnu.org/licenses/. 15 | */ 16 | 17 | #ifndef MULTI_CORE_H 18 | #define MULTI_CORE_H 19 | 20 | #include 21 | #include 22 | 23 | #include "multi_shared.h" 24 | 25 | //Current valid configuration options 26 | #define ADDRESS "address" 27 | #define NETMASK "netmask" 28 | #define GATEWAY "gateway" 29 | #define METRIC "metric" 30 | #define PROTO "proto" 31 | 32 | //Macro for freeing memory allocated to store a pair 33 | #define DEL_KEY_VALUE(key, value) \ 34 | yaml_event_delete(&key); \ 35 | yaml_event_delete(&value); 36 | 37 | /* Internal thread used to synchronize thread startup */ 38 | struct multi_core_sync{ 39 | struct multi_config *mc; 40 | pthread_mutex_t sync_mutex; 41 | pthread_cond_t sync_cond; 42 | }; 43 | 44 | struct multi_config* multi_core_initialize_config(uint8_t *cfg_file, 45 | uint8_t unique); 46 | int32_t multi_core_send(int32_t sock_fd, uint8_t *buf, int32_t numbytes); 47 | pthread_t multi_start(struct multi_config *mc); 48 | #endif 49 | -------------------------------------------------------------------------------- /src/multi_dhcp_common.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Kristian Evensen 3 | * 4 | * This file is part of Multi Network Manager (MNM). MNM is free software: you 5 | * can redistribute it and/or modify it under the terms of the Lesser GNU 6 | * General Public License as published by the Free Software Foundation, either 7 | * version 3 of the License, or (at your option) any later version. 8 | * 9 | * MNM is distributed in the hope that it will be useful, but WITHOUT ANY 10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License along with 14 | * Network Device Listener. If not, see http://www.gnu.org/licenses/. 15 | */ 16 | 17 | #ifndef MULTI_DHCP_COMMON_H 18 | #define MULTI_DHCP_COMMON_H 19 | 20 | #include "multi_dhcp_constants.h" 21 | 22 | #include 23 | #include 24 | 25 | /* DHCP's magic cookie */ 26 | static const uint8_t multi_dhcp_vendcookie[] = { 99, 130, 83, 99 }; 27 | 28 | typedef enum { 29 | DHCP_OVERLOAD_NONE, 30 | DHCP_OVERLOAD_FILE, 31 | DHCP_OVERLOAD_SNAME, 32 | DHCP_OVERLOAD_BOTH 33 | } multi_dhcp_overload_opts; 34 | 35 | typedef enum{ 36 | INIT, 37 | INIT_REBOOT, 38 | SELECTING, 39 | REQUESTING, 40 | BOUND, 41 | REBOOTING, 42 | RENEWING, 43 | REBINDING, 44 | DECLINE 45 | 46 | } multi_dhcp_state; 47 | 48 | /* config received from server, mostly via options */ 49 | struct multi_dhcp_config { 50 | /* timestamp we received the message */ 51 | struct timeval recvtime; 52 | 53 | /* server address */ 54 | struct in_addr dhcpd_addr; 55 | 56 | /* parsed options */ 57 | struct in_addr address; 58 | struct in_addr netmask; 59 | struct in_addr broadcast; 60 | 61 | struct in_addr gateway; 62 | 63 | struct in_addr dns[MAXOPTS]; 64 | unsigned short dns_num; 65 | 66 | char hostname[HOST_NAME_MAX]; 67 | char domainname[HOST_NAME_MAX]; 68 | 69 | unsigned int t1; 70 | unsigned int t2; 71 | unsigned int lease; 72 | 73 | char dhcpmsgtype; 74 | }; 75 | 76 | struct multi_dhcp_message { 77 | uint8_t *pos, *last; 78 | multi_dhcp_overload_opts overload, currentblock; 79 | 80 | /* embedded DHCP message */ 81 | uint8_t op; 82 | uint8_t htype; 83 | uint8_t hlen; 84 | uint8_t hops; 85 | uint32_t xid; 86 | uint16_t secs; 87 | uint16_t flags; 88 | uint32_t ciaddr; 89 | uint32_t yiaddr; 90 | uint32_t siaddr; 91 | uint32_t giaddr; 92 | uint8_t chaddr[16]; 93 | uint8_t sname[64]; 94 | uint8_t file[128]; 95 | uint8_t options[MAX_OPT_LEN]; 96 | } __attribute__((packed)); 97 | 98 | struct multi_dhcp_info{ 99 | uint32_t xid; 100 | struct sockaddr mac_addr; 101 | uint32_t ifidx; 102 | uint32_t raw_sock; 103 | uint32_t udp_sock; //Used for unicast-messages 104 | multi_dhcp_state state; 105 | struct multi_dhcp_config cfg; 106 | 107 | /* Used for the different timeouts */ 108 | uint32_t req_sent_time; 109 | uint32_t req_retrans; //All these are expressed as absolute values 110 | uint32_t t1; 111 | uint32_t t2; 112 | uint32_t lease; 113 | uint8_t retrans_count; 114 | //Says wheter or not a NEW timer event has started, to make output nicer 115 | uint8_t output_timer; 116 | }; 117 | 118 | #endif 119 | -------------------------------------------------------------------------------- /src/multi_dhcp_constants.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Kristian Evensen 3 | * 4 | * This file is part of Multi Network Manager (MNM). MNM is free software: you 5 | * can redistribute it and/or modify it under the terms of the Lesser GNU 6 | * General Public License as published by the Free Software Foundation, either 7 | * version 3 of the License, or (at your option) any later version. 8 | * 9 | * MNM is distributed in the hope that it will be useful, but WITHOUT ANY 10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License along with 14 | * Network Device Listener. If not, see http://www.gnu.org/licenses/. 15 | */ 16 | 17 | #ifndef MULTI_DHCP_CONSTANTS_H 18 | #define MULTI_DHCP_CONSTANTS_H 19 | 20 | #define MAX_OPT_LEN 512 // RFC 2131 21 | 22 | // RFC 2132, 3.3 23 | #define BOOTP_OPTION_NETMASK 1 24 | // RFC 2132, 3.5 25 | #define BOOTP_OPTION_GATEWAY 3 26 | // RFC 2132, 3.8 27 | #define BOOTP_OPTION_DNS 6 28 | // RFC 2132, 3.14 29 | #define BOOTP_OPTION_HOSTNAME 12 30 | // RFC 2132, 3.15 31 | #define BOOTP_OPTION_BOOTFILE_SIZE 13 32 | // RFC 2132, 3.17 33 | #define BOOTP_OPTION_DOMAIN 15 34 | // RFC 2132, 5.3 35 | #define BOOTP_OPTION_BROADCAST 28 36 | // RFC 2132, 8.1 37 | #define BOOTP_OPTION_NISDOMAIN 40 38 | 39 | // RFC 2132, 3.9 40 | #define DHCP_OPTION_LOGSRVS 7 41 | // RFC 2132, 3.11 42 | #define DHCP_OPTION_LPRSRVS 9 43 | // RFC 2132, 8.3 44 | #define DHCP_OPTION_NTPSRVS 42 45 | // RFC 2132, 8.9 46 | #define DHCP_OPTION_XFNTSRVS 48 47 | // RFC 2132, 8.10 48 | #define DHCP_OPTION_XDMSRVS 49 49 | // RFC 2132, 9.1 50 | #define DHCP_OPTION_REQADDR 50 51 | // RFC 2132, 9.2 52 | #define DHCP_OPTION_LEASE 51 53 | // RFC 2132, 9.3 54 | #define DHCP_OPTION_OVERLOAD 52 55 | // RFC 2132, 9.6 56 | #define DHCP_OPTION_TYPE 53 57 | // RFC 2132, 9.7 58 | #define DHCP_OPTION_SERVER 54 59 | // RFC 2132, 9.8 60 | #define DHCP_OPTION_OPTIONREQ 55 61 | // RFC 2132, 9.10 62 | #define DHCP_OPTION_MAXSIZE 57 63 | // RFC 2132, 9.11 64 | #define DHCP_OPTION_T1 58 65 | // RFC 2132, 9.12 66 | #define DHCP_OPTION_T2 59 67 | // RFC 2132, 9.13 68 | #define DHCP_OPTION_CLASS_IDENTIFIER 60 69 | // RFC 2132, 9.14 70 | #define DHCP_OPTION_CLIENT_IDENTIFIER 61 71 | // RFC 4039 72 | #define DHCP_OPTION_RAPID_COMMIT 80 73 | 74 | #define BOOTP_CLIENT_PORT 68 75 | #define BOOTP_SERVER_PORT 67 76 | //THIS IS ONLY FOR LITTLE ENDIAN 77 | #define BOOTP_PORT_PAIR ((BOOTP_SERVER_PORT << 16) + BOOTP_CLIENT_PORT) 78 | 79 | #define BOOTP_OPCODE_REQUEST 1 80 | #define BOOTP_OPCODE_REPLY 2 81 | 82 | #define NORESPONSE -10 83 | #define DHCP_TYPE_DISCOVER 1 84 | #define DHCP_TYPE_OFFER 2 85 | #define DHCP_TYPE_REQUEST 3 86 | #define DHCP_TYPE_DECLINE 4 87 | #define DHCP_TYPE_ACK 5 88 | #define DHCP_TYPE_NAK 6 89 | #define DHCP_TYPE_RELEASE 7 90 | #define DHCP_TYPE_INFORM 8 91 | 92 | #define MAXOPTS 3 93 | //#ifndef HOST_NAME_MAX 94 | //#warning HOST_NAME_MAX not defined, setting to 64 (using uclibc?) 95 | #define HOST_NAME_MAX 64 96 | //#endif 97 | 98 | #endif 99 | -------------------------------------------------------------------------------- /src/multi_dhcp_main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Kristian Evensen 3 | * 4 | * This file is part of Multi Network Manager (MNM). MNM is free software: you 5 | * can redistribute it and/or modify it under the terms of the Lesser GNU 6 | * General Public License as published by the Free Software Foundation, either 7 | * version 3 of the License, or (at your option) any later version. 8 | * 9 | * MNM is distributed in the hope that it will be useful, but WITHOUT ANY 10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License along with 14 | * Network Device Listener. If not, see http://www.gnu.org/licenses/. 15 | */ 16 | 17 | #include "multi_dhcp_common.h" 18 | #include "multi_dhcp_constants.h" 19 | #include "multi_dhcp_main.h" 20 | #include "multi_dhcp_protocol.h" 21 | #include "multi_dhcp_network.h" 22 | #include "multi_link_shared.h" 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | extern void multi_dhcp_create_dhcp_msg(struct multi_dhcp_info *di); 36 | extern int32_t multi_dhcp_recv_msg(struct multi_dhcp_info *di, 37 | struct multi_dhcp_message *dhcp_msg); 38 | extern void multi_dhcp_parse_dhcp_msg(struct multi_dhcp_info *di, 39 | struct multi_dhcp_message *dm, struct multi_link_info *li); 40 | 41 | static void multi_dhcp_setup_random() { 42 | int32_t fd; 43 | uint32_t seed = time(NULL); 44 | 45 | fd = open("/dev/urandom", O_RDONLY); 46 | 47 | if (fd != -1) { 48 | read(fd, &seed, sizeof(seed)); 49 | close(fd); 50 | } 51 | 52 | srand(seed); 53 | } 54 | 55 | /* This is the state machine for timeouts */ 56 | static void multi_dhcp_event_loop(struct multi_dhcp_info *di, 57 | struct multi_link_info *li){ 58 | fd_set master, read_fds; 59 | uint32_t fdmax; 60 | int32_t retval; 61 | struct timeval tv; 62 | uint32_t next_to, t_now; //next_to stores the next timeout, while t_now is used as the base value (all times are absolute) 63 | char buffer[1500]; //The DHCP-client never cares about data received on the UDP socket 64 | struct multi_dhcp_message dhcp_msg; 65 | 66 | /* Initialize select */ 67 | FD_ZERO(&master); 68 | FD_ZERO(&read_fds); 69 | FD_SET(di->raw_sock, &master); 70 | FD_SET(di->udp_sock, &master); 71 | FD_SET(li->decline_pipe[0], &master); 72 | fdmax = di->raw_sock > di->udp_sock ? di->raw_sock : di->udp_sock; 73 | fdmax = fdmax > li->decline_pipe[0] ? fdmax : li->decline_pipe[0]; 74 | 75 | /* Create and send the initial DHCP Discover */ 76 | multi_dhcp_create_dhcp_msg(di); 77 | 78 | while (1){ 79 | read_fds = master; 80 | //Used as offset for the time values, since everything is absolute 81 | //values 82 | t_now = time(NULL); 83 | 84 | /* These three states are simple, as there is no competing deadline 85 | * (they are all first step in a state change) */ 86 | if(di->state == SELECTING || di->state == REQUESTING || 87 | di->state == REBOOTING || di->state == DECLINE){ 88 | next_to = di->req_retrans; 89 | } else if (di->state == BOUND) { 90 | next_to = di->t1; 91 | } else if (di->state == RENEWING) { 92 | /* For both renewing and rebinding, there is a competing timeout */ 93 | next_to = di->req_retrans < di->t2 ? di->req_retrans : di->t2; 94 | } else if (di->state == REBINDING){ 95 | next_to = di->req_retrans < di->lease ? di->req_retrans : di->lease; 96 | } 97 | 98 | /* INIT allows packet should be sent ASAP (can get in INIT after for 99 | * example NAK), so no timer */ 100 | if(di->state != INIT && t_now < next_to){ 101 | /* Which timeout to use is derived from the current DHCP state. 102 | * All values are absolute, so subtract time now */ 103 | tv.tv_sec = next_to - t_now; 104 | tv.tv_usec = 0; 105 | 106 | if(di->output_timer){ 107 | MULTI_DEBUG_PRINT_SYSLOG(stderr,"Next timeout will expire in %u sec " 108 | "on interface %s (iface idx %u)\n", 109 | (uint32_t) tv.tv_sec, li->dev_name, li->ifi_idx); 110 | di->output_timer = 0; 111 | } 112 | 113 | retval = select(fdmax + 1, &read_fds, NULL, NULL, &tv); 114 | } else { 115 | //If the timer is larger than or equal to timeout, means that 116 | //timeout has occured 117 | retval = 0; 118 | } 119 | 120 | //Decline requires me to wait 10 seconds, then return to INIT. INIT 121 | //normally has no timeout. Thus, I have to sleep, then update state. 122 | if(di->state == DECLINE) 123 | di->state = INIT; 124 | 125 | if(retval == 0){ 126 | di->retrans_count++; //Start at 0, to get expected behavior 127 | 128 | if(di->retrans_count > REBOOTING_THRESHOLD){ 129 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Could not get a reply after %u" 130 | "timeouts, falling back to INIT/REBOOTING for interface" 131 | "%s\n", di->retrans_count, li->dev_name); 132 | 133 | //Try reboot, so I at least will have higher chance of keeping IP 134 | di->state = (di->state == BOUND || di->state == RENEWING) ? 135 | REBOOTING : INIT; 136 | di->retrans_count = 0; 137 | di->req_sent_time = 0; 138 | multi_dhcp_create_dhcp_msg(di); 139 | continue; 140 | } 141 | 142 | switch(di->state){ 143 | case INIT: 144 | case SELECTING: 145 | case REQUESTING: 146 | case REBOOTING: 147 | /* If REBOOTING fails a sufficient number of times, */ 148 | if(di->retrans_count > REBOOTING_THRESHOLD){ 149 | MULTI_DEBUG_PRINT_SYSLOG(stderr,"Rebooting threshold reached," 150 | "going back to INIT.\n"); 151 | di->state = INIT; 152 | di->retrans_count = 0; 153 | di->req_sent_time = 0; 154 | } 155 | multi_dhcp_create_dhcp_msg(di); 156 | break; 157 | case BOUND: 158 | case RENEWING: 159 | t_now = time(NULL); 160 | 161 | if(t_now >= di->t2){ 162 | MULTI_DEBUG_PRINT_SYSLOG(stderr,"T2 has expired for %s (iface" 163 | "idx %u)\n", li->dev_name, li->ifi_idx); 164 | di->retrans_count = 0; //Switch state 165 | di->state = REBINDING; 166 | } 167 | 168 | multi_dhcp_create_dhcp_msg(di); 169 | break; 170 | case REBINDING: 171 | /* Need a check for lease, if lease has expired */ 172 | if(t_now >= di->lease){ 173 | MULTI_DEBUG_PRINT_SYSLOG(stderr,"Lease has expired for %s" 174 | "(iface idx %u)\n", li->dev_name, li->ifi_idx); 175 | di->retrans_count = 0; 176 | di->req_sent_time = 0; 177 | di->state = INIT; 178 | 179 | /* Update link state and notify link module */ 180 | pthread_mutex_lock(&(li->state_lock)); 181 | //Only send if seend once 182 | if(li->state != DHCP_IP_INVALID && li->state 183 | != LINK_INVALID){ 184 | li->state = DHCP_IP_INVALID; 185 | multi_dhcp_notify_link_module(li->write_pipe); 186 | } 187 | pthread_mutex_unlock(&(li->state_lock)); 188 | } 189 | 190 | multi_dhcp_create_dhcp_msg(di); 191 | default: 192 | break; 193 | } 194 | } else { 195 | if(FD_ISSET(di->raw_sock, &read_fds)){ 196 | memset(&dhcp_msg, 0, sizeof(dhcp_msg)); 197 | 198 | multi_dhcp_recv_msg(di, &dhcp_msg); 199 | multi_dhcp_parse_dhcp_msg(di, &dhcp_msg, li); 200 | } else if(FD_ISSET(di->udp_sock, &read_fds)){ 201 | recv(di->udp_sock, buffer, 1500, 0); 202 | } else if(FD_ISSET(li->decline_pipe[0], &read_fds)){ 203 | read(li->decline_pipe[0], buffer, 1500); 204 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Must decline IP for %s\n", 205 | li->dev_name); 206 | di->state = DECLINE; 207 | multi_dhcp_create_dhcp_msg(di); 208 | } 209 | } 210 | } 211 | } 212 | 213 | static void multi_dhcp_cleanup(void *arg){ 214 | struct multi_dhcp_info *di = (struct multi_dhcp_info *) arg; 215 | 216 | /* Send release */ 217 | 218 | /* Close sockets */ 219 | if(di->raw_sock > 0) 220 | close(di->raw_sock); 221 | 222 | if(di->udp_sock > 0) 223 | close(di->udp_sock); 224 | 225 | MULTI_DEBUG_PRINT_SYSLOG(stderr,"Finished DHCP cleanup for interface with index " 226 | "%u. Sent RELEASE and closed sockets %u and %u.\n", di->ifidx, 227 | di->raw_sock, di->udp_sock); 228 | } 229 | 230 | void* multi_dhcp_main(void *arg){ 231 | struct multi_link_info *li = (struct multi_link_info *) arg; 232 | struct multi_dhcp_info di; 233 | 234 | /* Currently, this thread can be cancelled at any time */ 235 | pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); 236 | pthread_cleanup_push(multi_dhcp_cleanup, &di); 237 | 238 | /* Initializes the random number generator */ 239 | multi_dhcp_setup_random(); 240 | 241 | memset(&di, 0, sizeof(di)); 242 | 243 | if(li->state == REBOOT_DHCP){ 244 | di.state = INIT_REBOOT; 245 | di.cfg.address = li->cfg.address; 246 | } else { 247 | di.state = INIT; 248 | } 249 | 250 | //The transaction ID is a random number chosen by the client 251 | di.xid = rand(); 252 | di.output_timer = 0; 253 | 254 | if((di.raw_sock = multi_dhcp_create_raw_socket(li, &di)) == -1){ 255 | MULTI_DEBUG_PRINT_SYSLOG(stderr,"Failed to create raw socket for %s\n", 256 | li->dev_name); 257 | return; 258 | } 259 | 260 | if((di.udp_sock = multi_dhcp_create_udp_socket(li)) == -1){ 261 | MULTI_DEBUG_PRINT_SYSLOG(stderr,"Failed to create UDP socket for %s\n", 262 | li->dev_name); 263 | return; 264 | } 265 | 266 | if(di.state == REBOOT_DHCP){ 267 | MULTI_DEBUG_PRINT_SYSLOG(stderr,"Waiting for DHCP REBOOT on interface %s " 268 | "(iface idx %u). RAW socket: %u UDP socket: %u\n", li->dev_name, 269 | li->ifi_idx, di.raw_sock, di.udp_sock); 270 | } else{ 271 | MULTI_DEBUG_PRINT_SYSLOG(stderr,"Waiting for DHCP on interface %s (iface idx " 272 | "%u). RAW socket: %u UDP socket: %u\n", li->dev_name, 273 | li->ifi_idx, di.raw_sock, di.udp_sock); 274 | } 275 | 276 | multi_dhcp_event_loop(&di, li); 277 | 278 | pthread_cleanup_pop(1); /* Cleanup when failed too */ 279 | pthread_exit(NULL); 280 | } 281 | -------------------------------------------------------------------------------- /src/multi_dhcp_main.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Kristian Evensen 3 | * 4 | * This file is part of Multi Network Manager (MNM). MNM is free software: you 5 | * can redistribute it and/or modify it under the terms of the Lesser GNU 6 | * General Public License as published by the Free Software Foundation, either 7 | * version 3 of the License, or (at your option) any later version. 8 | * 9 | * MNM is distributed in the hope that it will be useful, but WITHOUT ANY 10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License along with 14 | * Network Device Listener. If not, see http://www.gnu.org/licenses/. 15 | */ 16 | 17 | #ifndef MULTI_DHCP_MAIN_H 18 | #define MULTI_DHCP_MAIN_H 19 | 20 | #define MAX_RETRANS 10 //Crappy Simula DHCP 21 | #define REBOOTING_THRESHOLD 2 22 | 23 | void* multi_dhcp_main(void *arg); 24 | 25 | #endif 26 | -------------------------------------------------------------------------------- /src/multi_dhcp_network.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Kristian Evensen 3 | * 4 | * This file is part of Multi Network Manager (MNM). MNM is free software: you 5 | * can redistribute it and/or modify it under the terms of the Lesser GNU 6 | * General Public License as published by the Free Software Foundation, either 7 | * version 3 of the License, or (at your option) any later version. 8 | * 9 | * MNM is distributed in the hope that it will be useful, but WITHOUT ANY 10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License along with 14 | * Network Device Listener. If not, see http://www.gnu.org/licenses/. 15 | */ 16 | 17 | #include "multi_dhcp_network.h" 18 | #include "multi_dhcp_constants.h" 19 | #include "multi_common.h" 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | /* taken from iputils which basically copied it from RFC 1071 */ 38 | static uint16_t multi_dhcp_in_cksum(const uint16_t *addr, register int len, 39 | uint16_t csum){ 40 | int nleft = len; 41 | const uint16_t *w = addr; 42 | uint16_t answer; 43 | int sum = csum; 44 | 45 | /* 46 | * Our algorithm is simple, using a 32 bit accumulator (sum), 47 | * we add sequential 16 bit words to it, and at the end, fold 48 | * back all the carry bits from the top 16 bits into the lower 49 | * 16 bits. 50 | */ 51 | while (nleft > 1) { 52 | sum += *w++; 53 | nleft -= 2; 54 | } 55 | 56 | /* mop up an odd byte, if necessary */ 57 | if (nleft == 1) 58 | sum += htons(*(u_char *)w << 8); 59 | 60 | /* 61 | * add back carry outs from top 16 bits to low 16 bits 62 | */ 63 | sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */ 64 | sum += (sum >> 16); /* add carry */ 65 | answer = ~sum; /* truncate to 16 bits */ 66 | return (answer); 67 | } 68 | 69 | void multi_dhcp_notify_link_module(int32_t pipe_fd){ 70 | if(write(pipe_fd, "a", 1) < 0){ 71 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Could not notify link module\n"); 72 | } 73 | } 74 | 75 | int multi_dhcp_snd_msg_raw(int sock, struct in_addr from_ip, int from_if, 76 | struct multi_dhcp_message *msg, uint8_t broadcast) { 77 | int length = msg->pos - &msg->op; 78 | uint16_t checksum; 79 | 80 | struct { 81 | struct iphdr ip; 82 | struct udphdr udp; 83 | } hdr; 84 | 85 | struct sockaddr_ll addr = { 86 | .sll_family = AF_PACKET, 87 | .sll_protocol = htons(ETH_P_IP), 88 | .sll_ifindex = from_if, 89 | .sll_hatype = 0, 90 | .sll_pkttype = 0, 91 | .sll_halen = ETH_ALEN 92 | }; 93 | 94 | struct iovec iov[] = { 95 | { .iov_base = &hdr, .iov_len = sizeof(hdr) }, 96 | { .iov_base = &msg->op, .iov_len = length } 97 | }; 98 | 99 | struct msghdr msghdr = { 100 | .msg_name = &addr, 101 | .msg_namelen = sizeof(addr), 102 | .msg_iov = iov, 103 | .msg_iovlen = 2, 104 | .msg_control = NULL, 105 | .msg_controllen = 0, 106 | .msg_flags = 0 107 | }; 108 | 109 | memset(addr.sll_addr, 255, ETH_ALEN); /* broadcast */ 110 | 111 | hdr.ip.version = 4; 112 | hdr.ip.ihl = 5; /* minimal 20 byte header w/o options */ 113 | hdr.ip.tos = 0; 114 | hdr.ip.tot_len = htons(sizeof(hdr.ip) + sizeof(hdr.udp) + length); 115 | hdr.ip.id = 0; 116 | hdr.ip.frag_off = htons(IP_DF); //Don't fragment 117 | hdr.ip.ttl = 64; 118 | hdr.ip.protocol = IPPROTO_UDP; 119 | 120 | if(broadcast){ 121 | hdr.ip.saddr = from_ip.s_addr; 122 | hdr.ip.daddr = 0xffffffff; 123 | } else { 124 | hdr.ip.saddr = msg->ciaddr; 125 | hdr.ip.daddr = from_ip.s_addr; 126 | } 127 | 128 | hdr.ip.check = 0; 129 | hdr.ip.check = multi_dhcp_in_cksum((u_short *)&hdr.ip, 20, hdr.ip.check); 130 | 131 | hdr.udp.source = htons(BOOTP_CLIENT_PORT); 132 | hdr.udp.dest = htons(BOOTP_SERVER_PORT); 133 | hdr.udp.len = htons(length + sizeof(struct udphdr)); 134 | hdr.udp.check = 0; // set to 0 for calculation 135 | 136 | checksum = htons(IPPROTO_UDP); 137 | checksum = multi_dhcp_in_cksum((u_short *)&hdr.udp.len, 2, checksum); 138 | checksum = multi_dhcp_in_cksum((u_short *)&hdr.ip.saddr, 16, ~checksum); 139 | checksum = multi_dhcp_in_cksum((u_short *)&msg->op, length, ~checksum); 140 | 141 | hdr.udp.check = checksum; 142 | 143 | return sendmsg(sock, &msghdr, 0); 144 | } 145 | 146 | int multi_dhcp_snd_msg_udp(int sock, struct in_addr *to, 147 | struct multi_dhcp_message *msg) { 148 | struct sockaddr_in toadr; 149 | int length = msg->pos - &msg->op; 150 | 151 | toadr.sin_family = AF_INET; 152 | toadr.sin_port = htons(BOOTP_SERVER_PORT); 153 | toadr.sin_addr = *to; 154 | return sendto(sock, &msg->op, length, 0, (const struct sockaddr *)&toadr, 155 | sizeof(toadr)); 156 | } 157 | 158 | int32_t multi_dhcp_recv_msg(struct multi_dhcp_info *di, 159 | struct multi_dhcp_message *dhcp_msg){ 160 | char dframe[ETH_DATA_LEN]; 161 | struct sockaddr_ll addr; 162 | socklen_t addrsize = sizeof(addr); 163 | ssize_t plen; 164 | 165 | struct iphdr *iph; 166 | struct udphdr *udph; 167 | uint8_t *dhcp_payload; 168 | 169 | memset(dhcp_msg, 0, sizeof(*dhcp_msg)); 170 | 171 | plen = recvfrom(di->raw_sock, dframe, sizeof(dframe), 0, 172 | (struct sockaddr *)&addr, &addrsize); 173 | 174 | if (plen == -1) 175 | return -1; 176 | 177 | if(ntohs(addr.sll_protocol) == ETH_P_ARP){ 178 | MULTI_DEBUG_PRINT_SYSLOG(stderr,"Got an ARP-message\n"); 179 | return -1; 180 | } 181 | 182 | iph = (struct iphdr *) dframe; 183 | 184 | if(iph->protocol != IPPROTO_UDP){ 185 | fprintf(stderr, "Received something that is not UDP\n"); 186 | return -1; 187 | } 188 | 189 | udph = (struct udphdr *) (iph + 1); 190 | 191 | /* Since I don't have an IP, port is the only thing I can check for */ 192 | if (udph->source != htons(BOOTP_SERVER_PORT)){ 193 | fprintf(stderr, "Server port is wrong.\n"); 194 | return -1; 195 | } 196 | 197 | if (udph->dest != htons(BOOTP_CLIENT_PORT)){ 198 | fprintf(stderr, "Client port is wrong\n"); 199 | return -1; 200 | } 201 | 202 | dhcp_payload = (uint8_t *) (udph + 1); 203 | memcpy(&(dhcp_msg->op), dhcp_payload, ntohs(udph->len) - 204 | sizeof(struct udphdr)); 205 | 206 | /* No DHCP */ 207 | if(memcmp(&(dhcp_msg->options), multi_dhcp_vendcookie, 4)){ 208 | MULTI_DEBUG_PRINT_SYSLOG(stderr,"Vendor cookie was not equal, this is not a" 209 | "valid DHCP packet\n"); 210 | return -1; 211 | } 212 | 213 | /* First value is casted! The reason for adding 4 is that the first four 214 | * bytes of the option field is the vendor cookie */ 215 | dhcp_msg->pos = (uint8_t*) &(dhcp_msg->options) + 4; 216 | dhcp_msg->last = (uint8_t*) &(dhcp_msg->op) + (ntohs(udph->len) - 217 | sizeof(struct udphdr)) - 1; //Remember, offset 218 | dhcp_msg->overload = DHCP_OVERLOAD_NONE; 219 | dhcp_msg->currentblock = DHCP_OVERLOAD_NONE; 220 | 221 | //parse_dhcp_msg(di, &dhcp_msg); 222 | 223 | return 1; 224 | } 225 | 226 | int32_t multi_dhcp_create_raw_socket(struct multi_link_info *li, 227 | struct multi_dhcp_info *di){ 228 | struct ifreq ifq; 229 | int32_t sockfd = 0; 230 | struct sockaddr_ll sll; 231 | 232 | memset(&sll, 0, sizeof(sll)); 233 | memset(&ifq, 0, sizeof(ifq)); 234 | memcpy(&ifq.ifr_name, li->dev_name, strlen((char*) li->dev_name) + 1); 235 | 236 | /* Since I am (currently) not interested in the L2-header, SOCK_DGRAM is 237 | * used and the L2-header is removed by kernel (man 7 packet). */ 238 | if((sockfd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP))) == -1){ 239 | //perror("Error creating raw socket:"); 240 | MULTI_DEBUG_PRINT_SYSLOG(stderr,"Error creating raw socket.\n"); 241 | return -1; 242 | } 243 | 244 | /* Use this opertunity to get MAC-address of this interface. Stored in the 245 | * chaddr-field of every DHCP packet */ 246 | if(ioctl(sockfd, SIOCGIFHWADDR, &ifq) == -1){ 247 | //perror("Could not get info on interface"); 248 | MULTI_DEBUG_PRINT_SYSLOG(stderr,"Could not get info on interface\n"); 249 | close(sockfd); 250 | return -1; 251 | } 252 | 253 | //Create the filter 254 | static const struct sock_filter only_dhcp[] = { 255 | //The best reference to BPF is the original paper, as well as the 256 | //example I found in LinuxJournal 257 | 258 | //Load the protocol into the accumulator (absolute position 9) 259 | BPF_STMT(BPF_LD | BPF_B | BPF_ABS, 9), 260 | //Compare accumulator with k, 261 | BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, IPPROTO_UDP, 0, 3), 262 | //MSH is this 4*[k]0xf syntax, a hack for the IP header. Load it into counter 263 | BPF_STMT(BPF_LDX | BPF_B | BPF_MSH, 0), 264 | //Load the source and destionation port into accumulator, pos. relative to counter 265 | BPF_STMT(BPF_LD | BPF_W | BPF_IND, 0), 266 | //Compare both ports at once 267 | BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, BOOTP_PORT_PAIR, 1, 0), 268 | 269 | //Return statements 270 | //Ignore the packet 271 | BPF_STMT(BPF_RET | BPF_K, 0), 272 | //Let the packet through (the value is the number of bytes to let 273 | //through, set to high value) 274 | BPF_STMT(BPF_RET | BPF_K, 0xffffffff), 275 | }; 276 | 277 | static const struct sock_fprog only_dhcp_prog = { 278 | .filter = (struct sock_filter *) only_dhcp, 279 | .len = sizeof(only_dhcp) / sizeof(only_dhcp[0]), 280 | }; 281 | 282 | if(setsockopt(sockfd, SOL_SOCKET, SO_ATTACH_FILTER, &only_dhcp_prog, 283 | sizeof(only_dhcp_prog)) == -1){ 284 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Could not attach netlink filter\n"); 285 | perror("Msg: "); 286 | close(sockfd); 287 | return -1; 288 | } 289 | 290 | memcpy(&(di->mac_addr), &(ifq.ifr_hwaddr.sa_data), 291 | sizeof(ifq.ifr_hwaddr.sa_data)); 292 | 293 | //Will be moved to main, does not belong here 294 | di->ifidx = li->ifi_idx; 295 | 296 | sll.sll_family = AF_PACKET; 297 | sll.sll_protocol = htons(ETH_P_IP); 298 | sll.sll_ifindex = li->ifi_idx; 299 | 300 | if(bind(sockfd, (struct sockaddr *) &sll, sizeof(sll)) == -1){ 301 | close(sockfd); 302 | MULTI_DEBUG_PRINT_SYSLOG(stderr,"Could not bind socket\n"); 303 | return -1; 304 | } 305 | 306 | return sockfd; 307 | } 308 | 309 | int multi_dhcp_create_udp_socket(struct multi_link_info *li) { 310 | struct sockaddr_in addr; 311 | int sock; 312 | int i = 1; 313 | 314 | sock = socket(AF_INET, SOCK_DGRAM, 0); 315 | if (sock == -1){ 316 | //perror("Could not establish UDP socket"); 317 | MULTI_DEBUG_PRINT_SYSLOG(stderr,"Could not establish UDP socket\n"); 318 | return -1; 319 | } 320 | 321 | if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &i, sizeof(i))){ 322 | //perror("Could not set UDP socket to broadcast"); 323 | MULTI_DEBUG_PRINT_SYSLOG(stderr,"Could not set UDP socket to broadcast\n"); 324 | return -1; 325 | } 326 | 327 | if(setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, li->dev_name, 328 | strlen((char*) li->dev_name) + 1) < 0){ 329 | MULTI_DEBUG_PRINT_SYSLOG(stderr,"Could not bind sock to interface %s " 330 | "(idx %u)\n", li->dev_name, li->ifi_idx); 331 | return -1; 332 | } 333 | 334 | addr.sin_family = AF_INET; 335 | addr.sin_addr.s_addr = 0; 336 | addr.sin_port = htons(BOOTP_CLIENT_PORT); 337 | 338 | if (bind(sock, (const struct sockaddr *)&addr, sizeof(addr))){ 339 | close(sock); 340 | //perror("Could not bind socket to port"); 341 | MULTI_DEBUG_PRINT_SYSLOG(stderr,"Could not bind socket to port for interface " 342 | "%s (idx %u) Error: %s\n", li->dev_name, li->ifi_idx, strerror(errno)); 343 | return -1; 344 | } 345 | 346 | if(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i)) < 0) 347 | MULTI_DEBUG_PRINT_SYSLOG(stderr,"Could not set SO_REUSEADDR on socket %d for " 348 | "interface %s (idx %u)\n", sock, li->dev_name, li->ifi_idx); 349 | 350 | return sock; 351 | } 352 | 353 | -------------------------------------------------------------------------------- /src/multi_dhcp_network.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Kristian Evensen 3 | * 4 | * This file is part of Multi Network Manager (MNM). MNM is free software: you 5 | * can redistribute it and/or modify it under the terms of the Lesser GNU 6 | * General Public License as published by the Free Software Foundation, either 7 | * version 3 of the License, or (at your option) any later version. 8 | * 9 | * MNM is distributed in the hope that it will be useful, but WITHOUT ANY 10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License along with 14 | * Network Device Listener. If not, see http://www.gnu.org/licenses/. 15 | */ 16 | 17 | #ifndef MULTI_DHCP_NETWORK_H 18 | #define MULTI_DHCP_NETWORK_H 19 | 20 | #include "multi_dhcp_common.h" 21 | #include "multi_link_shared.h" 22 | 23 | #include 24 | #include 25 | 26 | /* Used to send messages over the raw socket. If broadcast is set to one, then 27 | * from_ip is src ip. Otherwise, src is gathered from client address in the dhcp 28 | * msg (case only relevant after succesful BIND) */ 29 | 30 | int32_t multi_dhcp_snd_msg_raw(int sock, struct in_addr from_ip, int from_if, 31 | struct multi_dhcp_message *msg, uint8_t broadcast); 32 | 33 | /* Send a message unicast. Used to renew a lease, as the initial messages are 34 | * sent directly to the server */ 35 | int32_t multi_dhcp_snd_msg_udp(int sock, struct in_addr *to, 36 | struct multi_dhcp_message *msg); 37 | 38 | /* Receives DHCP messages. The message is stored in ms and function returns 0 on 39 | * success, -1 otherwise */ 40 | int32_t multi_dhcp_recv_msg(struct multi_dhcp_info *di, 41 | struct multi_dhcp_message *dhcp_msg); 42 | 43 | /* Creates RAW and UDP socket. In order to avoid redundant calls, di is included 44 | * to store the interface index and mac-addr */ 45 | int32_t multi_dhcp_create_raw_socket(struct multi_link_info *li, 46 | struct multi_dhcp_info *di); 47 | int multi_dhcp_create_udp_socket(struct multi_link_info *li); 48 | 49 | /* Helter that currently pushes one byte through the pipe to notify the link 50 | * module. Will maybe contain more info in future versions */ 51 | void multi_dhcp_notify_link_module(int32_t pipe_fd); 52 | 53 | #endif 54 | -------------------------------------------------------------------------------- /src/multi_dhcp_protocol.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Kristian Evensen 3 | * 4 | * This file is part of Multi Network Manager (MNM). MNM is free software: you 5 | * can redistribute it and/or modify it under the terms of the Lesser GNU 6 | * General Public License as published by the Free Software Foundation, either 7 | * version 3 of the License, or (at your option) any later version. 8 | * 9 | * MNM is distributed in the hope that it will be useful, but WITHOUT ANY 10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License along with 14 | * Network Device Listener. If not, see http://www.gnu.org/licenses/. 15 | */ 16 | 17 | #include "multi_dhcp_common.h" 18 | #include "multi_dhcp_constants.h" 19 | #include "multi_dhcp_protocol.h" 20 | #include "multi_dhcp_network.h" 21 | #include "multi_link_shared.h" 22 | #include "multi_common.h" 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | extern int32_t multi_dhcp_snd_msg_raw(int sock, struct in_addr from_ip, 38 | int from_if, struct multi_dhcp_message *msg, uint8_t broadcast); 39 | extern int32_t multi_dhcp_snd_msg_udp(int sock, struct in_addr *to, 40 | struct multi_dhcp_message *msg); 41 | 42 | /* Functions used to intialize dhcp message, add option, return next option 43 | * (used for parsing) and writing the last byte of the message. */ 44 | //Initializes the message and sets the cookie, cookie defined in RFC 45 | static void multi_dhcp_dm_init(struct multi_dhcp_message *msg) { 46 | memset(msg, 0, sizeof(*msg)); 47 | msg->pos = msg->options+4; 48 | memcpy(msg->options, multi_dhcp_vendcookie, 4); 49 | } 50 | 51 | /* Helper for adding option */ 52 | static void multi_dhcp_dm_add_option(struct multi_dhcp_message *msg, 53 | uint8_t option, uint8_t length, void *opt) { 54 | uint8_t *pos = msg->pos; 55 | 56 | if (&msg->options[MAX_OPT_LEN] - pos < length + 2) 57 | abort(); 58 | 59 | *pos++ = option; 60 | *pos++ = length; 61 | memcpy(pos, opt, length); 62 | pos += length; 63 | 64 | msg->pos = pos; 65 | } 66 | 67 | static uint8_t *multi_dhcp_dm_next_option(struct multi_dhcp_message *msg) { 68 | uint8_t *pos = msg->pos; 69 | uint8_t length; 70 | 71 | /* End of packet */ 72 | if (pos >= msg->last) 73 | return NULL; 74 | 75 | /* skip pad packets */ 76 | while(!*pos) if (++pos >= msg->last) 77 | return NULL; 78 | 79 | /* End of option marker */ 80 | while (*pos == 255) { 81 | /* Overload option handling */ 82 | if (msg->currentblock < msg->overload) { // currentblock: 0,1,3 83 | msg->currentblock++; 84 | 85 | if (msg->overload & DHCP_OVERLOAD_FILE & msg->currentblock) { 86 | pos = &msg->file[0]; 87 | msg->last = &msg->file[128]; 88 | } else { // SNAME or BOTH 89 | pos = &msg->sname[0]; 90 | msg->last = &msg->sname[64]; 91 | msg->currentblock = DHCP_OVERLOAD_BOTH; // definitely last block 92 | } 93 | 94 | /* skip pad packets */ 95 | while(!*pos) if (++pos >= msg->last) 96 | return NULL; 97 | } else { 98 | return NULL; 99 | } 100 | } 101 | 102 | /* Actually, this is extra paranoia. Even if pos+1 103 | * leaves the multi_dhcp_message structure, the next 104 | * check would catch this as long as we don't 105 | * try to access an unmapped page ;-) 106 | */ 107 | if (pos+1 >= msg->last) 108 | return NULL; 109 | 110 | length = *(pos+1); 111 | /* Length overflow */ 112 | if (pos + length + 2 > msg->last) 113 | return NULL; 114 | 115 | msg->pos = pos + length+2; 116 | return pos; 117 | } 118 | 119 | //The end option is 255 120 | static void multi_dhcp_dm_finish_options(struct multi_dhcp_message *msg) { 121 | if (msg->pos == &msg->options[MAX_OPT_LEN]) abort(); 122 | 123 | *msg->pos++ = 255; 124 | } 125 | 126 | void multi_dhcp_create_dhcp_msg(struct multi_dhcp_info *di){ 127 | struct multi_dhcp_message dhcp_msg; 128 | uint8_t dhcp_type; 129 | uint8_t iface_id[7]; 130 | struct in_addr ipaddr; 131 | uint32_t lease_time = ~0; 132 | //uint32_t lease_time = htonl(60); 133 | uint32_t t_now; 134 | uint8_t ip_addr[INET_ADDRSTRLEN]; 135 | FILE *hostname_file; 136 | uint8_t hostname[HOST_NAME_MAX + 1] = {0}; 137 | char *hostname_parsed = NULL; 138 | size_t retval; 139 | 140 | //Read hostname, we don't care if we fail (most important is to get lease) 141 | hostname_file = fopen("/etc/hostname", "r"); 142 | 143 | if (hostname_file != NULL) { 144 | retval = fread(hostname, 1, HOST_NAME_MAX, hostname_file); 145 | 146 | if (ferror(hostname_file)) { 147 | MULTI_DEBUG_PRINT_SYSLOG(stderr,"Failed to read hostname\n"); 148 | } else { 149 | hostname_parsed = strtok(hostname, "\r\n"); 150 | MULTI_DEBUG_PRINT_SYSLOG(stderr,"Hostname: %s\n", hostname_parsed); 151 | } 152 | 153 | fclose(hostname_file); 154 | } else { 155 | MULTI_DEBUG_PRINT_SYSLOG(stderr,"Failed to open hostname file\n"); 156 | } 157 | 158 | multi_dhcp_dm_init(&dhcp_msg); 159 | //Unique ID to separate DHCP requests from one another 160 | dhcp_msg.xid = di->xid; 161 | dhcp_msg.op = BOOTP_OPCODE_REQUEST; 162 | //Found in RFC1700, seems to be a default value for all Ethernet-standards 163 | dhcp_msg.htype = 1; 164 | //Length of MAC-address 165 | dhcp_msg.hlen = 6; 166 | memcpy(dhcp_msg.chaddr, &(di->mac_addr), 6); 167 | 168 | t_now = time(NULL); 169 | 170 | /* Which message to send depends on the client's state. Also, changes state 171 | * if needed */ 172 | switch(di->state){ 173 | case INIT: 174 | case SELECTING: 175 | /* If I get here, it is either the first packet or a timeout has 176 | * occured */ 177 | di->state = SELECTING; 178 | dhcp_type = DHCP_TYPE_DISCOVER; 179 | ipaddr.s_addr = 0; 180 | multi_dhcp_dm_add_option(&dhcp_msg, DHCP_OPTION_TYPE, 1, 181 | &dhcp_type); 182 | if (hostname_parsed) 183 | multi_dhcp_dm_add_option(&dhcp_msg, BOOTP_OPTION_HOSTNAME, 184 | strlen(hostname_parsed), hostname_parsed); 185 | 186 | /* According to RFC, it is time of ORIGINAL request */ 187 | if(di->req_sent_time == 0) 188 | di->req_sent_time = t_now; 189 | 190 | di->req_retrans = t_now + (4 * (di->retrans_count + 1)); 191 | MULTI_DEBUG_PRINT_SYSLOG(stderr,"Sending DHCP DISCOVER (iface idx %u).\n", 192 | di->ifidx); 193 | di->output_timer = 1; 194 | break; 195 | case INIT_REBOOT: 196 | case REBOOTING: 197 | di->state = REBOOTING; 198 | ipaddr.s_addr = 0; 199 | dhcp_type = DHCP_TYPE_REQUEST; 200 | multi_dhcp_dm_add_option(&dhcp_msg, DHCP_OPTION_TYPE, 1, 201 | &dhcp_type); 202 | multi_dhcp_dm_add_option(&dhcp_msg, DHCP_OPTION_REQADDR, 4, 203 | &di->cfg.address.s_addr); 204 | if (hostname_parsed) 205 | multi_dhcp_dm_add_option(&dhcp_msg, BOOTP_OPTION_HOSTNAME, 206 | strlen(hostname_parsed), hostname_parsed); 207 | 208 | /* As always, this is a NEW request so time will be set for the 209 | * first packet */ 210 | if(di->req_sent_time == 0) 211 | di->req_sent_time = t_now; 212 | 213 | di->req_retrans = t_now + (4 * (di->retrans_count + 1)); 214 | 215 | inet_ntop(AF_INET, &di->cfg.address, (char*) ip_addr, INET_ADDRSTRLEN); 216 | MULTI_DEBUG_PRINT_SYSLOG(stderr,"REBOOTING and requesting %s, Sending " 217 | "DHCP REQUEST (iface idx %u).\n", ip_addr, di->ifidx); 218 | di->output_timer = 1; 219 | break; 220 | case REQUESTING: 221 | ipaddr.s_addr = 0; 222 | dhcp_type = DHCP_TYPE_REQUEST; 223 | multi_dhcp_dm_add_option(&dhcp_msg, DHCP_OPTION_TYPE, 1, 224 | &dhcp_type); 225 | multi_dhcp_dm_add_option(&dhcp_msg, DHCP_OPTION_REQADDR, 4, 226 | &di->cfg.address.s_addr); 227 | multi_dhcp_dm_add_option(&dhcp_msg, DHCP_OPTION_SERVER, 4, 228 | &di->cfg.dhcpd_addr.s_addr); 229 | if (hostname_parsed) 230 | multi_dhcp_dm_add_option(&dhcp_msg, BOOTP_OPTION_HOSTNAME, 231 | strlen(hostname_parsed), hostname_parsed); 232 | 233 | //Not updating req_sent_time, as this is just a step in a request 234 | di->req_retrans = t_now + (4 * (di->retrans_count + 1)); 235 | MULTI_DEBUG_PRINT_SYSLOG(stderr,"Sending DHCP REQUEST (iface idx %u).\n", 236 | di->ifidx); 237 | di->output_timer = 1; 238 | break; 239 | case BOUND: 240 | case RENEWING: 241 | di->state = RENEWING; 242 | ipaddr = di->cfg.dhcpd_addr; //Messages for renewing is sent unicast 243 | dhcp_type = DHCP_TYPE_REQUEST; 244 | multi_dhcp_dm_add_option(&dhcp_msg, DHCP_OPTION_TYPE, 1, 245 | &dhcp_type); 246 | if (hostname_parsed) 247 | multi_dhcp_dm_add_option(&dhcp_msg, BOOTP_OPTION_HOSTNAME, 248 | strlen(hostname_parsed), hostname_parsed); 249 | dhcp_msg.ciaddr = di->cfg.address.s_addr; 250 | 251 | /* As always, this is a NEW request so time will be set for the 252 | * first packet */ 253 | if(di->req_sent_time == 0) 254 | di->req_sent_time = t_now; 255 | 256 | di->req_retrans = t_now + (4 * (di->retrans_count + 1)); 257 | MULTI_DEBUG_PRINT_SYSLOG(stderr,"RENEWING, sending DHCP REQUEST (iface " 258 | "idx %u).\n", di->ifidx); 259 | di->output_timer = 1; 260 | break; 261 | case REBINDING: 262 | di->state = REBINDING; 263 | //According to RFC, the old IP must be stored here 264 | ipaddr = di->cfg.address; 265 | dhcp_type = DHCP_TYPE_REQUEST; 266 | multi_dhcp_dm_add_option(&dhcp_msg, DHCP_OPTION_TYPE, 1, 267 | &dhcp_type); 268 | if (hostname_parsed) 269 | multi_dhcp_dm_add_option(&dhcp_msg, BOOTP_OPTION_HOSTNAME, 270 | strlen(hostname_parsed), hostname_parsed); 271 | dhcp_msg.ciaddr = di->cfg.address.s_addr; 272 | 273 | di->req_retrans = t_now + (4 * (di->retrans_count + 1)); 274 | MULTI_DEBUG_PRINT_SYSLOG(stderr,"REBINDING, sending DHCP REQUEST " 275 | "(iface idx %u).\n", di->ifidx); 276 | di->output_timer = 1; 277 | break; 278 | case DECLINE: 279 | dhcp_type = DHCP_TYPE_DECLINE; 280 | multi_dhcp_dm_add_option(&dhcp_msg, DHCP_OPTION_TYPE, 1, 281 | &dhcp_type); 282 | multi_dhcp_dm_add_option(&dhcp_msg, DHCP_OPTION_REQADDR, 4, 283 | &di->cfg.address.s_addr); 284 | if (hostname_parsed) 285 | multi_dhcp_dm_add_option(&dhcp_msg, BOOTP_OPTION_HOSTNAME, 286 | strlen(hostname_parsed), hostname_parsed); 287 | 288 | //According to the RFC, move back to init 289 | //di->state = INIT; 290 | di->retrans_count = 0; 291 | di->req_sent_time = 0; 292 | di->req_retrans = t_now + 10; //Timeout after decline is 10 seconds 293 | break; 294 | default: 295 | break; 296 | } 297 | 298 | /* Must be done manually due to the pair (see RFC2132) */ 299 | iface_id[0] = 1; 300 | memcpy(iface_id+1, &(di->mac_addr), 6); 301 | multi_dhcp_dm_add_option(&dhcp_msg, DHCP_OPTION_CLIENT_IDENTIFIER, 7, 302 | iface_id); 303 | 304 | /* The server will only reply when I specify what info I want, naturally. However, RFC states it should return something, check at work tomorrow */ 305 | if(dhcp_type == DHCP_TYPE_DISCOVER || dhcp_type == DHCP_TYPE_REQUEST){ 306 | multi_dhcp_dm_add_option(&dhcp_msg, DHCP_OPTION_OPTIONREQ, 307 | sizeof(multi_dhcp_optlist), multi_dhcp_optlist); 308 | multi_dhcp_dm_add_option(&dhcp_msg, DHCP_OPTION_LEASE, 309 | sizeof(lease_time), &lease_time); 310 | } 311 | 312 | multi_dhcp_dm_finish_options(&dhcp_msg); 313 | 314 | //Send packet. I suspect that the reason I don't receive a unicast reply is 315 | //because the interface don't have a source IP set 316 | if(di->state == RENEWING){ 317 | multi_dhcp_snd_msg_udp(di->udp_sock, &ipaddr, &dhcp_msg); 318 | } else { 319 | multi_dhcp_snd_msg_raw(di->raw_sock, ipaddr, di->ifidx, &dhcp_msg, 1); 320 | } 321 | } 322 | 323 | /* Copied from dhclient and helper to get values from the option field */ 324 | /* Inspired from asm-generic/unaligned.h. gcc3.3.5 and 4.1.2 325 | * optimize well on i386 */ 326 | struct multi_dhcp_u32 { u_int32_t x __attribute__((packed)); }; 327 | static inline u_int32_t multi_dhcp_get_unaligned32(const u_int32_t *addr) 328 | { 329 | const struct multi_dhcp_u32 *ptr = (const struct multi_dhcp_u32 *) addr; 330 | return ptr->x; 331 | } 332 | 333 | static void multi_dhcp_parse_options(struct multi_dhcp_message *msg, 334 | struct multi_dhcp_config *cfg) { 335 | u_int8_t *opt; 336 | 337 | memset(cfg, 0, sizeof(*cfg)); 338 | 339 | cfg->address.s_addr = msg->yiaddr; 340 | cfg->dhcpd_addr.s_addr = msg->siaddr; 341 | 342 | while((opt = multi_dhcp_dm_next_option(msg))) { 343 | u_int8_t *optdata = opt+2; 344 | u_int8_t optsize = *(opt+1); 345 | u_int8_t m; 346 | 347 | switch(*opt) { 348 | case DHCP_OPTION_TYPE: 349 | if (optsize == 1) 350 | cfg->dhcpmsgtype = *optdata; 351 | break; 352 | case DHCP_OPTION_SERVER: 353 | if (optsize == 4) 354 | cfg->dhcpd_addr.s_addr = 355 | multi_dhcp_get_unaligned32((u_int32_t *)optdata); 356 | break; 357 | case BOOTP_OPTION_NETMASK: 358 | if (optsize == 4) 359 | cfg->netmask.s_addr = 360 | multi_dhcp_get_unaligned32((u_int32_t *)optdata); 361 | break; 362 | case BOOTP_OPTION_GATEWAY: 363 | if (optsize >= 4) 364 | cfg->gateway.s_addr = 365 | multi_dhcp_get_unaligned32((u_int32_t *)optdata); 366 | break; 367 | case BOOTP_OPTION_DNS: 368 | if (!(optsize & 3)) { 369 | u_int8_t n; 370 | 371 | m = optsize / 4; 372 | if (m > MAXOPTS) m = MAXOPTS; 373 | cfg->dns_num = m; 374 | 375 | for (n=0; ndns[n].s_addr = 377 | multi_dhcp_get_unaligned32((u_int32_t *)(optdata+4*n)); 378 | } 379 | break; 380 | case BOOTP_OPTION_HOSTNAME: 381 | if (optsize >= sizeof(cfg->hostname)) 382 | optsize = sizeof(cfg->hostname)-1; 383 | memcpy(cfg->hostname, optdata, optsize); 384 | cfg->hostname[optsize] = 0; 385 | break; 386 | case BOOTP_OPTION_DOMAIN: 387 | if (optsize >= sizeof(cfg->domainname)) 388 | optsize = sizeof(cfg->domainname)-1; 389 | memcpy(cfg->domainname, optdata, optsize); 390 | cfg->domainname[optsize] = 0; 391 | break; 392 | case BOOTP_OPTION_BROADCAST: 393 | if (optsize == 4) 394 | cfg->broadcast.s_addr = 395 | multi_dhcp_get_unaligned32((u_int32_t *)optdata); 396 | break; 397 | case DHCP_OPTION_LEASE: 398 | if (optsize == 4) 399 | cfg->lease = 400 | ntohl(multi_dhcp_get_unaligned32((u_int32_t *)optdata)); 401 | break; 402 | case DHCP_OPTION_OVERLOAD: 403 | if (optsize == 1 && *optdata <= DHCP_OVERLOAD_BOTH) 404 | msg->overload = *optdata; 405 | break; 406 | case DHCP_OPTION_T1: 407 | if (optsize == 4) 408 | cfg->t1 = 409 | ntohl(multi_dhcp_get_unaligned32((u_int32_t *)optdata)); 410 | break; 411 | case DHCP_OPTION_T2: 412 | if (optsize == 4) 413 | cfg->t2 = 414 | ntohl(multi_dhcp_get_unaligned32((u_int32_t *)optdata)); 415 | break; 416 | } 417 | } 418 | } 419 | 420 | void multi_dhcp_parse_dhcp_msg(struct multi_dhcp_info *di, 421 | struct multi_dhcp_message *dm, struct multi_link_info *li){ 422 | struct multi_dhcp_config cfg; 423 | uint32_t t_now, t_diff; 424 | uint8_t ipaddr[INET_ADDRSTRLEN]; 425 | uint8_t baddr[INET_ADDRSTRLEN]; 426 | uint8_t gwaddr[INET_ADDRSTRLEN]; 427 | uint8_t netmask[INET_ADDRSTRLEN]; 428 | 429 | multi_dhcp_parse_options(dm, &cfg); 430 | 431 | if (dm->xid != di->xid || dm->hlen != 6 || 432 | memcmp(dm->chaddr, &(di->mac_addr),6)){ 433 | //fprintf(stderr, "Message intended for someone else!\n"); 434 | return; 435 | } 436 | 437 | switch(di->state){ 438 | case SELECTING: 439 | //One typical scenario here is if the lease expires before the 440 | //DHCP ACK for final REBIND is received 441 | if(cfg.dhcpmsgtype != DHCP_TYPE_OFFER){ 442 | MULTI_DEBUG_PRINT_SYSLOG(stderr,"Mismatch state. In INIT but did not " 443 | "get OFFER. Got %u\n", cfg.dhcpmsgtype); 444 | return; 445 | } 446 | 447 | /* Move on to the next state, retrans count must be reset */ 448 | di->retrans_count = 0; 449 | 450 | MULTI_DEBUG_PRINT_SYSLOG(stderr,"Received DHCP OFFER on interface %s " 451 | "(iface idx %u), will send DHCP REQUEST\n", li->dev_name, 452 | li->ifi_idx); 453 | 454 | di->cfg = cfg; 455 | di->state = REQUESTING; 456 | multi_dhcp_create_dhcp_msg(di); 457 | break; 458 | case RENEWING: 459 | case REBINDING: 460 | case REQUESTING: 461 | case REBOOTING: 462 | /* All these states */ 463 | if(cfg.dhcpmsgtype == DHCP_TYPE_NAK){ 464 | /* According to the RFC, a NAK involves moving straight back to 465 | * INIT and resending request. Moving to INIT implies resetting 466 | * variables and state, just in case */ 467 | MULTI_DEBUG_PRINT_SYSLOG(stderr,"Got NAK in state %u. Resetting and " 468 | "retrying DISCOVER! (iface idx %u)\n", di->state, 469 | di->ifidx); 470 | di->state = INIT; 471 | di->req_sent_time = 0; 472 | //Since next packet is sent immideatly, this can 0 (as opposed 473 | //to -1 for ACK) 474 | di->retrans_count = 0; 475 | 476 | /* Set state as waiting. I can here if a) rebooting fails b) 477 | * requesting fails c) renewing fails d) rebinding fails. In the 478 | * last two, the link can be in UP state */ 479 | pthread_mutex_lock(&(li->state_lock)); 480 | li->state = WAITING_FOR_DHCP; 481 | pthread_mutex_unlock(&(li->state_lock)); 482 | 483 | multi_dhcp_create_dhcp_msg(di); 484 | } else if(cfg.dhcpmsgtype == DHCP_TYPE_ACK){ 485 | //Always decline DHCP address 486 | di->cfg = cfg; //Just in case, I know these are the good values 487 | 488 | di->state = BOUND; 489 | 490 | inet_ntop(AF_INET, &(cfg.address), (char*) ipaddr, INET_ADDRSTRLEN); 491 | inet_ntop(AF_INET, &(cfg.broadcast), (char*) baddr, INET_ADDRSTRLEN); 492 | inet_ntop(AF_INET, &(cfg.gateway), (char*) gwaddr, INET_ADDRSTRLEN); 493 | inet_ntop(AF_INET, &(cfg.netmask), (char*) netmask, INET_ADDRSTRLEN); 494 | 495 | //Do the timeout calculation. Be warned that inet_ntoa is NOT 496 | //reentrant. In other words, the IP adresses are wrong! 497 | MULTI_DEBUG_PRINT_SYSLOG(stderr,"Got DHCP ACK on interface %s " 498 | "(iface idx %u). %s will be bound to IP: %s Broadcast: " 499 | "%s Gateway: %s Netmask %s (%u) Lease: %u T1: %u T2: " 500 | "%u\n", li->dev_name, li->ifi_idx, li->dev_name, 501 | ipaddr, baddr, gwaddr, netmask, 502 | 32 - (ffs(ntohl(cfg.netmask.s_addr)) - 1), 503 | cfg.lease, cfg.t1, cfg.t2); 504 | 505 | 506 | //TODO: I need some variable or check to prevent adding the same IP twice. Compare cfg is maybe sufficient? Or at least address? 507 | pthread_mutex_lock(&(li->state_lock)); 508 | 509 | /* This is needed if one interface switches network. Otherwise, 510 | * the main thread will not know that it has to clean up (it 511 | * will just see a new set of addresses)! */ 512 | /* Need to wireless access points in order to test this, with 513 | * different subnets */ 514 | if(li->cfg.address.s_addr != 0 && 515 | (li->cfg.address.s_addr != cfg.address.s_addr || 516 | li->cfg.broadcast.s_addr != cfg.broadcast.s_addr || 517 | li->cfg.gateway.s_addr != cfg.gateway.s_addr || 518 | li->cfg.netmask.s_addr != cfg.netmask.s_addr)){ 519 | 520 | li->state = DHCP_IP_CHANGED; 521 | li->new_cfg = cfg; 522 | multi_dhcp_notify_link_module(li->write_pipe); 523 | } else{ 524 | li->cfg = cfg; 525 | 526 | /* This is correct becuase if the information has not 527 | * changed, then there is no need to update the state. The 528 | * cfg must be updated due to leases and so on */ 529 | if(li->state != LINK_UP){ 530 | li->state = GOT_IP_DHCP; 531 | multi_dhcp_notify_link_module(li->write_pipe); 532 | } 533 | } 534 | 535 | pthread_mutex_unlock(&(li->state_lock)); 536 | 537 | t_now = time(NULL); 538 | t_diff = t_now - di->req_sent_time; 539 | 540 | di->lease = cfg.lease; 541 | di->t1 = cfg.t1 ? cfg.t1 : cfg.lease / 2; 542 | di->t2 = cfg.t2 ? cfg.t2 : cfg.lease * 0.875; 543 | 544 | /* Not exactly sure what to do in this case */ 545 | assert(t_diff < di->t1 || t_diff < di->t2); 546 | assert(di->t1 < di->t2); 547 | 548 | /* Lease is from WHEN the request was sent */ 549 | di->lease -= t_diff; 550 | di->t1 -= t_diff; 551 | di->t2 -= t_diff; 552 | 553 | /* Convert values to be absolute */ 554 | di->lease += t_now; 555 | di->t1 += t_now; 556 | di->t2 += t_now; 557 | 558 | /* Every packet has been accounted for, so timers and everything can be reset */ 559 | di->req_sent_time = 0; 560 | //This will overflow, but it is ok. When the next timeout (T1) 561 | //is triggered, retrans_count will be increased by 1 and, thus, 562 | //be 0 again (but a little hackish) 563 | 564 | di->retrans_count = -1; 565 | /* New timeout event started */ 566 | di->output_timer = 1; 567 | } 568 | default: 569 | break; 570 | } 571 | } 572 | 573 | -------------------------------------------------------------------------------- /src/multi_dhcp_protocol.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Kristian Evensen 3 | * 4 | * This file is part of Multi Network Manager (MNM). MNM is free software: you 5 | * can redistribute it and/or modify it under the terms of the Lesser GNU 6 | * General Public License as published by the Free Software Foundation, either 7 | * version 3 of the License, or (at your option) any later version. 8 | * 9 | * MNM is distributed in the hope that it will be useful, but WITHOUT ANY 10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License along with 14 | * Network Device Listener. If not, see http://www.gnu.org/licenses/. 15 | */ 16 | 17 | #ifndef MULTI_DHCP_PROTOCOL_H 18 | #define MULTI_DHCP_PROTOCOL_H 19 | 20 | #include "multi_dhcp_common.h" 21 | #include "multi_dhcp_constants.h" 22 | #include "multi_link_shared.h" 23 | 24 | #include 25 | 26 | /* The options I am interested in (domain is just my domain, 27 | * for example simula.no) */ 28 | static char multi_dhcp_optlist[] = { 29 | BOOTP_OPTION_NETMASK, 30 | BOOTP_OPTION_GATEWAY, 31 | BOOTP_OPTION_DNS, 32 | BOOTP_OPTION_HOSTNAME, 33 | BOOTP_OPTION_DOMAIN, 34 | BOOTP_OPTION_BROADCAST, 35 | DHCP_OPTION_LEASE, 36 | DHCP_OPTION_T1, 37 | DHCP_OPTION_T2 38 | }; 39 | 40 | /* Creates a dhcp msg and sends it over the network through the interface 41 | * specified by di */ 42 | void multi_dhcp_create_dhcp_msg(struct multi_dhcp_info *di); 43 | 44 | /* Parses the options in msg and stores them in cfg */ 45 | static void multi_dhcp_parse_options(struct multi_dhcp_message *msg, 46 | struct multi_dhcp_config *cfg); 47 | 48 | /* Parses the message and stores relevant information in di */ 49 | void multi_dhcp_parse_dhcp_msg(struct multi_dhcp_info *di, 50 | struct multi_dhcp_message *dm, struct multi_link_info *li); 51 | 52 | #endif 53 | -------------------------------------------------------------------------------- /src/multi_link_core.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Kristian Evensen 3 | * 4 | * This file is part of Multi Network Manager (MNM). MNM is free software: you 5 | * can redistribute it and/or modify it under the terms of the Lesser GNU 6 | * General Public License as published by the Free Software Foundation, either 7 | * version 3 of the License, or (at your option) any later version. 8 | * 9 | * MNM is distributed in the hope that it will be useful, but WITHOUT ANY 10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License along with 14 | * Network Device Listener. If not, see http://www.gnu.org/licenses/. 15 | */ 16 | 17 | /* Generic includes */ 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include //netlink depends on this, but does not include it? 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | /* Netlink specific */ 32 | #include 33 | #include 34 | #include 35 | 36 | /* iwlib. REMEMBER to update this one to include linux/if.h */ 37 | #include 38 | 39 | /* Network helpers */ 40 | #include //Need this version of if.h because of Linux' link states 41 | #include 42 | 43 | /* Framework includes */ 44 | #include "multi_shared.h" 45 | #include "multi_core.h" 46 | #include "multi_link_core.h" 47 | #include "multi_link_shared.h" 48 | #include "multi_dhcp_main.h" 49 | #include "multi_link_netlink.h" 50 | #include "multi_link_filter.h" 51 | #include "multi_common.h" 52 | #include "multi_macros.h" 53 | #include "multi_cmp.h" 54 | 55 | /* See coments for where variable/function is defined */ 56 | //multi_shared 57 | extern struct multi_shared_static_links_list multi_shared_static_links; 58 | 59 | //multi_link_core 60 | extern int32_t multi_link_dhcp_pipes[2]; 61 | 62 | //multi_link_netlink 63 | extern void multi_link_configure_link(struct multi_link_info *li); 64 | extern void multi_link_remove_link(struct multi_link_info *li); 65 | extern void multi_link_remove_ppp(struct multi_link_info *li); 66 | extern void multi_link_remove_ap(struct multi_link_info *li); 67 | extern void multi_link_get_iface_info(struct multi_link_info *li); 68 | 69 | //multi_link_filter 70 | extern int32_t multi_link_fill_rtattr(const struct nlattr *attr, void *data); 71 | extern uint8_t multi_link_check_wlan_mode(uint8_t *dev_name); 72 | extern int32_t multi_link_filter_links(const struct nlmsghdr *nlh, void *data); 73 | extern int32_t multi_link_filter_ipaddr(const struct nlmsghdr *nlh, void *data); 74 | extern int32_t multi_link_filter_iprules(const struct nlmsghdr *nlh, 75 | void *data); 76 | extern int32_t multi_link_filter_iproutes(const struct nlmsghdr *nlh, 77 | void *data); 78 | extern void multi_link_free_ip_info(struct ip_info *ip_info); 79 | 80 | //multi_dhcp_main 81 | extern void* multi_dhcp_main(void *arg); 82 | 83 | static void multi_link_del_info(struct filter_list nlmsg_list, uint16_t nlmsg_type); 84 | 85 | int32_t multi_link_filter(uint32_t seq, mnl_cb_t cb, void *arg){ 86 | uint8_t buf[MNL_SOCKET_BUFFER_SIZE]; 87 | int32_t ret; 88 | uint32_t portid = mnl_socket_get_portid(multi_link_nl_request); 89 | 90 | ret = mnl_socket_recvfrom(multi_link_nl_request, buf, sizeof(buf)); 91 | 92 | while(ret > 0){ 93 | ret = mnl_cb_run(buf, ret, seq, portid, cb, arg); 94 | 95 | if(ret <= MNL_CB_STOP) 96 | break; 97 | 98 | ret = mnl_socket_recvfrom(multi_link_nl_request, buf, sizeof(buf)); 99 | } 100 | 101 | return ret; 102 | } 103 | 104 | 105 | static void multi_link_notify_probing(int32_t probe_pipe, uint32_t ifi_idx, 106 | link_state state){ 107 | uint8_t buffer[sizeof(ifi_idx) + 1]; 108 | 109 | buffer[0] = state; 110 | memcpy((buffer + 1), &ifi_idx, sizeof(ifi_idx)); 111 | if(write(probe_pipe, buffer, sizeof(buffer)) < 0){ 112 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Could not send link notification\n"); 113 | } 114 | } 115 | 116 | /* Check for PPP and call get_info */ 117 | //TODO: RENAME to something more generic 118 | static void multi_link_check_ppp(void* data, void* user_data){ 119 | struct multi_link_info *li = (struct multi_link_info *) data; 120 | 121 | if(li->state == LINK_DOWN_PPP){ 122 | multi_link_get_iface_info(li); 123 | 124 | //Need to do something smart here! Remover link or something! 125 | if(li->state != GOT_IP_PPP){ 126 | //Need to treat this in a special way! Remove li, keep 127 | //li and have some sort of timeout? 128 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Could not get info for PPP link %u " 129 | "(check_ppp)!\n", li->ifi_idx); 130 | } else{ 131 | multi_link_remove_ppp(li); 132 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Got info for PPP link %u " 133 | "(check_ppp)!\n", li->ifi_idx); 134 | } 135 | } else if(li->state == LINK_DOWN_AP) 136 | multi_link_get_iface_info(li); 137 | } 138 | 139 | //Return 0 if IP is not unique 140 | static uint8_t multi_link_check_unique(struct multi_link_info *li, 141 | uint8_t update){ 142 | struct multi_link_info *li_itr = NULL; 143 | uint8_t unique = 1; 144 | 145 | for(li_itr = multi_link_links_2.lh_first; li_itr != NULL; 146 | li_itr = li_itr->next.le_next){ 147 | //Avoid comparing li with li. Locking in case a dhcp thread is about to 148 | //change info 149 | pthread_mutex_lock(&(li->state_lock)); 150 | if(li->ifi_idx != li_itr->ifi_idx){ 151 | if(update) 152 | unique = !(li->new_cfg.address.s_addr == 153 | li_itr->new_cfg.address.s_addr); 154 | else 155 | unique = !(li->cfg.address.s_addr == 156 | li_itr->cfg.address.s_addr); 157 | } 158 | 159 | pthread_mutex_unlock(&(li->state_lock)); 160 | 161 | if(!unique) 162 | break; 163 | } 164 | 165 | return unique; 166 | } 167 | 168 | //Addresses and routes are always deleted when an interface goes down, but not 169 | //rules. Netlink is not reliable and there are scenarios where we will loose 170 | //messages and not clean up properly. We therefore need to make sure that there 171 | //exists no rules poining to this address/interface 172 | static int32_t multi_link_rules_sanity(struct multi_link_info *li) 173 | { 174 | struct ip_info ip_info; 175 | uint8_t buf[MNL_SOCKET_BUFFER_SIZE]; 176 | struct nlmsghdr *nlh; 177 | struct rtgenmsg *rt; 178 | uint32_t seq; 179 | int32_t ret; 180 | struct multi_link_filter_iprule filter_iprule = {0}; 181 | 182 | memset(buf, 0, MNL_SOCKET_BUFFER_SIZE); 183 | memset(&ip_info, 0, sizeof(ip_info)); 184 | 185 | //I am lazy and will use ip_info for now 186 | TAILQ_INIT(&(ip_info.ip_rules_n)); 187 | 188 | filter_iprule.li = li; 189 | ip_info.data = &filter_iprule; 190 | 191 | //Sets room for one nlmsghdr in buffer buf 192 | nlh = mnl_nlmsg_put_header(buf); 193 | nlh->nlmsg_type = RTM_GETRULE; 194 | nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; 195 | nlh->nlmsg_seq = seq = time(NULL); 196 | rt = mnl_nlmsg_put_extra_header(nlh, sizeof(struct rtgenmsg)); 197 | rt->rtgen_family = AF_INET; //Multi only supports v4 198 | 199 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Will check for dirty rules\n"); 200 | 201 | if(mnl_socket_sendto(multi_link_nl_request, nlh, nlh->nlmsg_len) < 0){ 202 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Cannot request rules dump\n"); 203 | return EXIT_FAILURE; 204 | } 205 | 206 | ret = multi_link_filter(seq, multi_link_filter_iprules_addr, &ip_info); 207 | 208 | if (ret) { 209 | //Some rules might have been added 210 | multi_link_free_ip_info(&ip_info); 211 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Sanity check failed (memory)\n"); 212 | return EXIT_FAILURE; 213 | } 214 | 215 | if (!ip_info.ip_rules_n.tqh_first) 216 | return EXIT_SUCCESS; 217 | 218 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Will delete dirty rules\n"); 219 | 220 | multi_link_del_info(ip_info.ip_rules_n, RTM_DELRULE); 221 | multi_link_free_ip_info(&ip_info); 222 | 223 | return EXIT_SUCCESS; 224 | } 225 | 226 | static void multi_link_check_link(void *data, void *user_data){ 227 | struct multi_link_info *li = (struct multi_link_info *) data; 228 | struct multi_config *mc = (struct multi_config *) user_data; 229 | int32_t probe_pipe = mc->socket_pipe[1]; 230 | 231 | pthread_mutex_lock(&(li->state_lock)); 232 | if(li->state == GOT_IP_DHCP || li->state == GOT_IP_STATIC_UP || 233 | li->state == GOT_IP_STATIC || li->state == GOT_IP_PPP || 234 | li->state == GOT_IP_AP){ 235 | 236 | /* Add routes */ 237 | //Check for uniqueness if needed 238 | if(li->state == GOT_IP_DHCP && mc->unique && 239 | !multi_link_check_unique(li, 0)){ 240 | li->state = WAITING_FOR_DHCP; 241 | memset(&li->cfg, 0, sizeof(li->cfg)); 242 | if(write(li->decline_pipe[1], "a", 1) < 0){ 243 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Could not decline IP\n"); 244 | } 245 | pthread_mutex_unlock(&(li->state_lock)); 246 | return; 247 | } 248 | 249 | /* Check for leftover rules */ 250 | multi_link_rules_sanity(li); 251 | 252 | //TODO: Add error checks! 253 | multi_link_configure_link(li); 254 | if(li->state == GOT_IP_STATIC_UP){ 255 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "IP address set for %s (iface idx %u)\n", 256 | li->dev_name, li->ifi_idx); 257 | //Do not advertise interfaces that are only UP, they can't be used 258 | //yet 259 | li->state = LINK_UP_STATIC_IFF; 260 | return; 261 | } else 262 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "IP address, routes and rules set for " 263 | "device %s (iface idx %u)\n", li->dev_name, li->ifi_idx); 264 | 265 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "M U %s %u %u\n", li->dev_name, 266 | li->ifi_idx, li->metric); 267 | multi_link_notify_probing(probe_pipe, li->ifi_idx, LINK_UP); 268 | 269 | if(li->state == GOT_IP_STATIC) 270 | li->state = LINK_UP_STATIC; 271 | else if(li->state == GOT_IP_PPP) 272 | li->state = LINK_UP_PPP; 273 | else if(li->state == GOT_IP_AP) 274 | li->state = LINK_UP_AP; 275 | else{ 276 | li->state = LINK_UP; 277 | } 278 | } else if(li->state == DHCP_IP_CHANGED){ 279 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Link %s changed IP\n", li->dev_name); 280 | 281 | if(mc->unique && !multi_link_check_unique(li, 1)){ 282 | li->state = WAITING_FOR_DHCP; 283 | if(write(li->decline_pipe[1], "a", 1) < 0){ 284 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Could not decline IP\n"); 285 | } 286 | pthread_mutex_unlock(&(li->state_lock)); 287 | return; 288 | } 289 | 290 | /* Delete and then add ip and routes */ 291 | multi_link_notify_probing(probe_pipe, li->ifi_idx, LINK_DOWN); 292 | multi_link_remove_link(li); 293 | 294 | //Configure the new routes 295 | li->cfg = li->new_cfg; 296 | multi_link_configure_link(li); 297 | li->state = LINK_UP; 298 | multi_link_notify_probing(probe_pipe, li->ifi_idx, LINK_UP); 299 | } else if (li->state == DHCP_IP_INVALID){ 300 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Link %s IP is marked as invalid\n", 301 | li->dev_name); 302 | //Delete information about link 303 | multi_link_remove_link(li); 304 | 305 | //Notify module that link is down (to an application, down and invalid 306 | //is the same) 307 | multi_link_notify_probing(probe_pipe, li->ifi_idx, LINK_DOWN); 308 | li->state = LINK_INVALID; 309 | } else if(li->state == DHCP_FAILED){ 310 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "DHCP has failed, will clean up and " 311 | "remove thread!\n"); 312 | 313 | /* Remove routes if link has an IP */ 314 | if(li->cfg.address.s_addr != 0){ 315 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Remove routes\n"); 316 | multi_link_remove_link(li); 317 | } 318 | 319 | li->state = DELETE_LINK; 320 | multi_link_notify_probing(probe_pipe, li->ifi_idx, LINK_DOWN); 321 | } 322 | 323 | pthread_mutex_unlock(&(li->state_lock)); 324 | } 325 | 326 | /* Remove all links that have been deleted. This is only used when 327 | * DHCP fails! */ 328 | static void multi_link_clean_links(){ 329 | struct multi_link_info *li, *li_tmp; 330 | 331 | for(li = multi_link_links_2.lh_first; li != NULL; ){ 332 | //I update the list while iterating, so I need to forward iterator 333 | li_tmp = li; 334 | li = li->next.le_next; 335 | 336 | /* No need for lock here, the state DELETE_LINK is ONLY set by this 337 | * thread and DHCP thread has been cancelled */ 338 | if(li_tmp->state == DELETE_LINK){ 339 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Will delete %s\n", li_tmp->dev_name); 340 | LIST_REMOVE(li_tmp, next); 341 | --multi_link_num_links; 342 | free(li_tmp); 343 | } 344 | } 345 | } 346 | 347 | struct multi_link_info *multi_link_create_new_link(uint8_t* dev_name, 348 | uint32_t metric){ 349 | struct multi_link_info *li = (struct multi_link_info *) 350 | malloc(sizeof(struct multi_link_info)); 351 | 352 | memset(li, 0, sizeof(struct multi_link_info)); 353 | 354 | //If a link is static, use the stored metric. Otherwise, first available 355 | if(metric > 0){ 356 | li->metric = metric; 357 | li->keep_metric = 1; 358 | } else { 359 | //Get the first available metric. I dont need to check error code, as 360 | //the check for num. links in modify_link ensures that there will always 361 | //be one metric available if the code gets here 362 | li->metric = ffs(~multi_shared_metrics_set); 363 | 364 | if (!li->metric) { 365 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "No available metrics\n"); 366 | free(li); 367 | return NULL; 368 | } 369 | 370 | //ffs starts indexing from 1 371 | multi_shared_metrics_set ^= 1 << (li->metric - 1); 372 | } 373 | 374 | li->state = WAITING_FOR_DHCP; 375 | li->ifi_idx = if_nametoindex((char*) dev_name); 376 | li->write_pipe = multi_link_dhcp_pipes[1]; 377 | memcpy(&(li->dev_name), dev_name, strlen((char*) dev_name)); 378 | pthread_mutex_init(&(li->state_lock), NULL); 379 | 380 | if(pipe(li->decline_pipe) < 0){ 381 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Could not create decline pipe\n"); 382 | free(li); 383 | return NULL; 384 | } 385 | 386 | return li; 387 | } 388 | 389 | static void multi_link_delete_link(struct multi_link_info *li, 390 | uint32_t probe_pipe){ 391 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "M D %s %u %u\n", li->dev_name, 392 | li->ifi_idx, li->metric); 393 | 394 | if(li->cfg.address.s_addr != 0) 395 | multi_link_remove_link(li); 396 | 397 | pthread_cancel(li->dhcp_thread); 398 | pthread_join(li->dhcp_thread, NULL); 399 | 400 | LIST_REMOVE(li, next); 401 | --multi_link_num_links; 402 | 403 | if(!li->keep_metric) 404 | //Remember that metric is one higher than index 405 | multi_shared_metrics_set ^= 1 << (li->metric-1); 406 | 407 | //Should maybe be done earlier 408 | multi_link_notify_probing(probe_pipe, li->ifi_idx, LINK_DOWN); 409 | 410 | if(li->decline_pipe[0] > 0){ 411 | close(li->decline_pipe[0]); 412 | close(li->decline_pipe[1]); 413 | } 414 | 415 | free(li); 416 | } 417 | 418 | static void multi_link_modify_link(const struct nlmsghdr *nlh, 419 | uint32_t probe_pipe, uint8_t unique){ 420 | struct ifinfomsg *ifi = mnl_nlmsg_get_payload(nlh); 421 | struct nlattr *tb[IFLA_MAX + 1] = {}; 422 | uint8_t iface_state = 0; 423 | struct multi_link_info *li = NULL; 424 | struct multi_link_info_static *li_static = NULL; 425 | pthread_attr_t detach_attr; 426 | uint8_t wireless_mode = 0; 427 | uint8_t *if_name; 428 | 429 | mnl_attr_parse(nlh, sizeof(*ifi), multi_link_fill_rtattr, tb); 430 | 431 | if (!tb[IFLA_IFNAME]) { 432 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Missing interface name\n"); 433 | return; 434 | } 435 | 436 | if_name = (uint8_t*) mnl_attr_get_str(tb[IFLA_IFNAME]); 437 | 438 | if (!strncmp(if_name, "veth", 4) || 439 | ifi->ifi_type == ARPHRD_VOID || 440 | (ifi->ifi_type == ARPHRD_NONE && strncmp(if_name,"nlw_", 4) && strncmp(if_name,"wwan", 4)) || 441 | ifi->ifi_type == ARPHRD_TUNNEL || 442 | ifi->ifi_flags & IFF_LOOPBACK) 443 | return; 444 | 445 | if(tb[IFLA_OPERSTATE]){ 446 | iface_state = mnl_attr_get_u8(tb[IFLA_OPERSTATE]); 447 | 448 | /* Check linux/Documentation/networking/operstates.txt. IFF_RUNNING 449 | * wraps both UP and UNKNOWN*/ 450 | if (ifi->ifi_flags & IFF_RUNNING){ 451 | //IF_OPER_UP == 6, defined in linux/if.h, chaos with includes 452 | //if(iface_state == 6){ 453 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Interface %s (%u) is RUNNING, " 454 | "length %u\n", if_name, ifi->ifi_index, 455 | multi_link_num_links); 456 | 457 | if((wireless_mode = multi_link_check_wlan_mode(if_name))) 458 | if(wireless_mode == 6){ 459 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Interface %s is monitor, " 460 | "ignoring\n", if_name); 461 | return; 462 | } 463 | 464 | LIST_FIND_CUSTOM(li, &multi_link_links_2, next, &(ifi->ifi_index), 465 | multi_cmp_ifidx); 466 | 467 | if(li != NULL){ 468 | if(li->state == LINK_UP_STATIC_IFF){ 469 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Interface %s (idx %u) has " 470 | "gone from UP to RUNNING\n", if_name, ifi->ifi_index); 471 | li->state = GOT_IP_STATIC; 472 | } else 473 | MULTI_DEBUG_PRINT_SYSLOG(stderr,"Interface %s (idx %u) has " 474 | "already been seen. Ignoring event\n", if_name, 475 | ifi->ifi_index); 476 | return; 477 | } 478 | 479 | if(multi_link_num_links < MAX_NUM_LINKS){ 480 | TAILQ_FIND_CUSTOM(li_static, &multi_shared_static_links, 481 | list_ptr, if_name, multi_cmp_devname); 482 | 483 | if(li_static != NULL){ 484 | if(li_static->proto == PROTO_IGNORE){ 485 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Ignoring %s\n", if_name); 486 | return; 487 | } else 488 | li = multi_link_create_new_link(if_name, 489 | li_static->metric); 490 | } else { 491 | /* Allocate a new link, add to list and start DHCP */ 492 | li = multi_link_create_new_link(if_name, 0); 493 | } 494 | 495 | if (!li) { 496 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Could not create link\n"); 497 | return; 498 | } 499 | 500 | //Insert link into link list 501 | LIST_INSERT_HEAD(&multi_link_links_2, li, next); 502 | ++multi_link_num_links; 503 | 504 | /* Add as a case here! The check for point to point */ 505 | if(li_static != NULL && li_static->proto == PROTO_STATIC){ 506 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Link %s found in static list\n", 507 | if_name); 508 | li->state = GOT_IP_STATIC; 509 | li->cfg = li_static->cfg_static; 510 | } else if (ifi->ifi_type == ARPHRD_PPP){ 511 | /* PPP will be dealt with separatley, since they get the IP 512 | * remotely by themself */ 513 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Link %s (%u) is PPP! state " 514 | "%u %u\n", if_name, ifi->ifi_index, iface_state, 515 | IFF_RUNNING); 516 | li->state = LINK_DOWN_PPP; 517 | multi_link_get_iface_info(li); 518 | 519 | if(li->state != GOT_IP_PPP){ 520 | //Need to treat this in a special way! Remove li, keep 521 | //li and have some sort of timeout? 522 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Could not get info for PPP " 523 | "link %u (first look)!\n", ifi->ifi_index); 524 | } else { 525 | //Clean the information that is automatically added to 526 | //routing table 527 | multi_link_remove_ppp(li); 528 | } 529 | 530 | } else if(wireless_mode){ 531 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Link %s is wlan access point\n", 532 | if_name); 533 | li->state = LINK_DOWN_AP; 534 | multi_link_get_iface_info(li); 535 | 536 | //Remove the automatic route 537 | multi_link_remove_ap(li); 538 | } else { 539 | pthread_attr_init(&detach_attr); 540 | pthread_attr_setdetachstate(&detach_attr, 541 | PTHREAD_CREATE_DETACHED); 542 | pthread_create(&(li->dhcp_thread), &detach_attr, 543 | multi_dhcp_main, (void *) li); 544 | } 545 | } else 546 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Limit reached, cannot add more links\n"); 547 | } else if(ifi->ifi_flags & IFF_UP){ //Might replace with IF_OPER_DOWN 548 | //Check if interface has already been seen 549 | LIST_FIND_CUSTOM(li, &multi_link_links_2, next, &(ifi->ifi_index), 550 | multi_cmp_ifidx); 551 | 552 | //Interface is already seen as UP, so clean up, no matter if static 553 | //or not. Static is a special case: remove routes, li from list 554 | //and free li 555 | if(li != NULL){ 556 | //Need a generic cleanup, move the next "else" into a separate 557 | //function 558 | MULTI_DEBUG_PRINT_SYSLOG(stderr,"Interface %s (idx %u) has already " 559 | "been seen as UP, will clean\n", if_name, ifi->ifi_index); 560 | multi_link_delete_link(li, probe_pipe); 561 | return; 562 | } 563 | 564 | //Check if interface is in static list 565 | TAILQ_FIND_CUSTOM(li_static, &multi_shared_static_links, list_ptr, 566 | if_name, multi_cmp_devname); 567 | 568 | if(li_static != NULL && li_static->proto == PROTO_STATIC){ 569 | //Allocate a new link 570 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Link %s is UP\n", if_name); 571 | li = multi_link_create_new_link(if_name, li_static->metric); 572 | 573 | if (li == NULL) { 574 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Failed to create new link\n"); 575 | return; 576 | } 577 | 578 | li->state = GOT_IP_STATIC_UP; 579 | li->cfg = li_static->cfg_static; 580 | LIST_INSERT_HEAD(&multi_link_links_2, li, next); 581 | ++multi_link_num_links; 582 | } 583 | } else { 584 | uint32_t dev_idx = ifi->ifi_index; 585 | LIST_FIND_CUSTOM(li, &multi_link_links_2, next, &dev_idx, 586 | multi_cmp_ifidx); 587 | 588 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Interface %s (index %u) is down, " 589 | "length %u\n", if_name, ifi->ifi_index, 590 | multi_link_num_links); 591 | 592 | if(li == NULL){ 593 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Could not find %s (index %u), " 594 | "length %u\n", if_name, ifi->ifi_index, 595 | multi_link_num_links); 596 | } else{ 597 | multi_link_delete_link(li, probe_pipe); 598 | } 599 | } 600 | } 601 | } 602 | 603 | static int32_t multi_link_parse_netlink(const struct nlmsghdr *nlh, void *data){ 604 | struct multi_config *mc = (struct multi_config*) data; 605 | 606 | if(nlh->nlmsg_type == RTM_NEWLINK || nlh->nlmsg_type == RTM_DELLINK) 607 | multi_link_modify_link(nlh, mc->socket_pipe[1], mc->unique); 608 | 609 | return MNL_CB_OK; 610 | } 611 | 612 | static void multi_link_populate_links_list(){ 613 | //MNL_SOCKET_BUFFER_SIZE is 8k, which is the max nlmsg size (see 614 | //linux/netlink.h) 615 | uint8_t buf[MNL_SOCKET_BUFFER_SIZE]; 616 | struct nlmsghdr *nlh; 617 | struct rtgenmsg *rt; 618 | uint32_t seq; 619 | 620 | memset(buf, 0, MNL_SOCKET_BUFFER_SIZE); 621 | 622 | //Sets room for one nlmsghdr in buffer buf 623 | nlh = mnl_nlmsg_put_header(buf); 624 | nlh->nlmsg_type = RTM_GETLINK; 625 | nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; 626 | nlh->nlmsg_seq = seq = time(NULL); //How will this work with event? Send 0? 627 | rt = mnl_nlmsg_put_extra_header(nlh, sizeof(struct rtgenmsg)); 628 | rt->rtgen_family = AF_UNSPEC; //I need all interfaces 629 | 630 | if(mnl_socket_sendto(multi_link_nl_request, nlh, nlh->nlmsg_len) < 0){ 631 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Cannot request info dump\n"); 632 | return; 633 | } 634 | 635 | multi_link_filter(seq, multi_link_filter_links, NULL); 636 | } 637 | 638 | static void multi_link_del_info(struct filter_list nlmsg_list, uint16_t nlmsg_type){ 639 | struct filter_msg *msg; 640 | struct nlmsghdr *nlh; 641 | uint32_t seq; 642 | 643 | for(msg = nlmsg_list.tqh_first; msg != NULL; msg = msg->list_ptr.tqe_next){ 644 | nlh = (struct nlmsghdr*) &(msg->nlh); 645 | nlh->nlmsg_type = nlmsg_type; 646 | nlh->nlmsg_flags = NLM_F_REQUEST; 647 | nlh->nlmsg_seq = seq = time(NULL); 648 | 649 | mnl_socket_sendto(multi_link_nl_request, nlh, nlh->nlmsg_len); 650 | } 651 | } 652 | 653 | static int32_t multi_link_flush_links(){ 654 | struct ip_info ip_info; 655 | uint8_t buf[MNL_SOCKET_BUFFER_SIZE]; 656 | struct nlmsghdr *nlh; 657 | struct rtgenmsg *rt; 658 | uint32_t seq; 659 | 660 | memset(buf, 0, MNL_SOCKET_BUFFER_SIZE); 661 | memset(&ip_info, 0, sizeof(ip_info)); 662 | 663 | //Initialize list 664 | TAILQ_INIT(&(ip_info.ip_addr_n)); 665 | TAILQ_INIT(&(ip_info.ip_rules_n)); 666 | TAILQ_INIT(&(ip_info.ip_routes_n)); 667 | 668 | //Sets room for one nlmsghdr in buffer buf 669 | nlh = mnl_nlmsg_put_header(buf); 670 | nlh->nlmsg_type = RTM_GETADDR; 671 | nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; 672 | nlh->nlmsg_seq = seq = time(NULL); //How will this work with event? Send 0? 673 | rt = mnl_nlmsg_put_extra_header(nlh, sizeof(struct rtgenmsg)); 674 | rt->rtgen_family = AF_INET; //I need all interfaces 675 | 676 | //Address 677 | if(mnl_socket_sendto(multi_link_nl_request, nlh, nlh->nlmsg_len) < 0){ 678 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Cannot request address dump\n"); 679 | return EXIT_FAILURE; 680 | } 681 | 682 | multi_link_filter(seq, multi_link_filter_ipaddr, &ip_info); 683 | 684 | //Rules 685 | nlh->nlmsg_type = RTM_GETRULE; 686 | if(mnl_socket_sendto(multi_link_nl_request, nlh, nlh->nlmsg_len) < 0){ 687 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Cannot request rules dump\n"); 688 | return EXIT_FAILURE; 689 | } 690 | 691 | multi_link_filter(seq, multi_link_filter_iprules, &ip_info); 692 | 693 | //Routes 694 | nlh->nlmsg_type = RTM_GETROUTE; 695 | if(mnl_socket_sendto(multi_link_nl_request, nlh, nlh->nlmsg_len) < 0){ 696 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Cannot request rules dump\n"); 697 | return EXIT_FAILURE; 698 | } 699 | 700 | multi_link_filter(seq, multi_link_filter_iproutes, &ip_info); 701 | 702 | /* Remove existing information and free memory */ 703 | multi_link_del_info(ip_info.ip_routes_n, RTM_DELROUTE); 704 | multi_link_del_info(ip_info.ip_rules_n, RTM_DELRULE); 705 | multi_link_del_info(ip_info.ip_addr_n, RTM_DELADDR); 706 | multi_link_free_ip_info(&ip_info); 707 | return EXIT_SUCCESS; 708 | } 709 | 710 | static int32_t multi_link_event_loop(struct multi_config *mc){ 711 | struct multi_link_info *li; 712 | pthread_attr_t detach_attr; 713 | uint8_t buf[MAX_PIPE_MSG_LEN]; 714 | uint8_t mnl_buf[MNL_SOCKET_BUFFER_SIZE]; 715 | int32_t retval, numbytes; 716 | uint32_t i; 717 | int32_t mnl_sock_event, mnl_sock_set, mnl_sock_get; 718 | fd_set masterfds, readfds; 719 | int fdmax = 0; 720 | struct timeval tv; 721 | 722 | FD_ZERO(&masterfds); 723 | FD_ZERO(&readfds); 724 | 725 | //NETLINK_ROUTE is where I want to hook into the kernel 726 | if(!(multi_link_nl_request = mnl_socket_open(NETLINK_ROUTE))){ 727 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Could not create mnl socket (request)\n"); 728 | return EXIT_FAILURE; 729 | } 730 | 731 | if(!(multi_link_nl_set = mnl_socket_open(NETLINK_ROUTE))){ 732 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Could not create mnl socket (set)\n"); 733 | return EXIT_FAILURE; 734 | } 735 | 736 | if(!(multi_link_nl_event = mnl_socket_open(NETLINK_ROUTE))){ 737 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Could not create mnl socket (event)\n"); 738 | return EXIT_FAILURE; 739 | } 740 | 741 | if(mnl_socket_bind(multi_link_nl_request, 0, MNL_SOCKET_AUTOPID) < 0){ 742 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Could not bind mnl event socket\n"); 743 | mnl_socket_close(multi_link_nl_event); 744 | return EXIT_FAILURE; 745 | } 746 | 747 | if(mnl_socket_bind(multi_link_nl_set, 0, MNL_SOCKET_AUTOPID) < 0){ 748 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Could not bind mnl event socket\n"); 749 | mnl_socket_close(multi_link_nl_event); 750 | return EXIT_FAILURE; 751 | } 752 | 753 | if(mnl_socket_bind(multi_link_nl_event, 1 << (RTNLGRP_LINK - 1), MNL_SOCKET_AUTOPID) 754 | < 0){ 755 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Could not bind mnl event socket\n"); 756 | mnl_socket_close(multi_link_nl_event); 757 | return EXIT_FAILURE; 758 | } 759 | 760 | if(pipe(multi_link_dhcp_pipes) < 0){ 761 | //perror("Pipe failed\n"); 762 | MULTI_DEBUG_PRINT_SYSLOG(stderr,"Pipe failed\n"); 763 | return EXIT_FAILURE; 764 | } 765 | 766 | /* Find interfaces that are already up, removes info and then reruns 767 | * DHCP (need config) */ 768 | multi_link_populate_links_list(); 769 | 770 | /* Check if I have any PPP links. */ 771 | //TODO: Give this one a better name since it is not only for PPP any more 772 | LIST_FOREACH_CB(&multi_link_links_2, next, multi_link_check_ppp, li, NULL); 773 | 774 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Done populating links list!\n"); 775 | 776 | if(multi_link_flush_links() == EXIT_FAILURE) 777 | return EXIT_FAILURE; 778 | 779 | //Go through already seen interfaces and start DHCP as needed 780 | if(multi_link_num_links > 0){ 781 | pthread_attr_init(&detach_attr); 782 | pthread_attr_setdetachstate(&detach_attr, PTHREAD_CREATE_DETACHED); 783 | 784 | for(li = multi_link_links_2.lh_first; li != NULL; 785 | li = li->next.le_next){ 786 | /* Start DHCP */ 787 | if(li->state == WAITING_FOR_DHCP){ 788 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Starting DHCP for existing " 789 | "interface %s\n", li->dev_name); 790 | pthread_create(&(li->dhcp_thread), &detach_attr, 791 | multi_dhcp_main, (void *) li); 792 | } 793 | } 794 | } 795 | 796 | /* Do a scan of the list here to check for links with static IP/PPP */ 797 | LIST_FOREACH_CB(&multi_link_links_2, next, multi_link_check_link, li, mc); 798 | 799 | mnl_sock_event = mnl_socket_get_fd(multi_link_nl_event); 800 | mnl_sock_set = mnl_socket_get_fd(multi_link_nl_set); 801 | mnl_sock_get = mnl_socket_get_fd(multi_link_nl_request); 802 | 803 | FD_SET(mnl_sock_event, &masterfds); 804 | fdmax = fdmax > mnl_sock_event ? fdmax : mnl_sock_event; 805 | FD_SET(mnl_sock_get, &masterfds); 806 | fdmax = fdmax > mnl_sock_get ? fdmax : mnl_sock_get; 807 | FD_SET(mnl_sock_set, &masterfds); 808 | fdmax = fdmax > mnl_sock_set ? fdmax : mnl_sock_set; 809 | FD_SET(multi_link_dhcp_pipes[0], &masterfds); 810 | fdmax = fdmax > multi_link_dhcp_pipes[0] ? fdmax : multi_link_dhcp_pipes[0]; 811 | 812 | tv.tv_sec = 5; 813 | tv.tv_usec = 0; 814 | 815 | while(1){ 816 | readfds = masterfds; 817 | 818 | retval = select(fdmax+1, &readfds, NULL, NULL, &tv); 819 | 820 | if(retval == 0){ 821 | //Check for any PPP that is marked as down 822 | LIST_FOREACH_CB(&multi_link_links_2, next, multi_link_check_ppp, 823 | li, NULL); 824 | LIST_FOREACH_CB(&multi_link_links_2, next, multi_link_check_link, 825 | li, mc); 826 | 827 | tv.tv_sec = 5; 828 | tv.tv_usec = 0; 829 | continue; 830 | } 831 | 832 | //TODO: Rewrite this so I only call the callbacks at the end, not per 833 | //message 834 | for(i=0; i<=fdmax; i++){ 835 | if(FD_ISSET(i, &readfds)){ 836 | if (i == mnl_sock_event){ 837 | numbytes = mnl_socket_recvfrom(multi_link_nl_event, 838 | mnl_buf, sizeof(mnl_buf)); 839 | mnl_cb_run(mnl_buf, numbytes, 0, 0, 840 | multi_link_parse_netlink, mc); 841 | LIST_FOREACH_CB(&multi_link_links_2, next, 842 | multi_link_check_link, li, mc); 843 | } else if (i == mnl_sock_set){ 844 | numbytes = mnl_socket_recvfrom(multi_link_nl_set, mnl_buf, 845 | sizeof(mnl_buf)); 846 | } else if (i == mnl_sock_get){ 847 | numbytes = mnl_socket_recvfrom(multi_link_nl_request, 848 | mnl_buf, sizeof(mnl_buf)); 849 | } else if (i == multi_link_dhcp_pipes[0]){ 850 | numbytes = read(i, buf, MAX_PIPE_MSG_LEN); 851 | LIST_FOREACH_CB(&multi_link_links_2, next, 852 | multi_link_check_link, li, mc); 853 | multi_link_clean_links(); 854 | } 855 | } 856 | } 857 | } 858 | } 859 | 860 | /* TODO: Configuration */ 861 | void* multi_link_module_init(void *arg){ 862 | struct multi_core_sync *mcs = (struct multi_core_sync *) arg; 863 | 864 | LIST_INIT(&multi_link_links_2); 865 | multi_link_num_links = 0; 866 | 867 | pthread_mutex_lock(&(mcs->sync_mutex)); 868 | pthread_cond_signal(&(mcs->sync_cond)); 869 | pthread_mutex_unlock(&(mcs->sync_mutex)); 870 | multi_link_event_loop(mcs->mc); 871 | 872 | return NULL; 873 | } 874 | 875 | -------------------------------------------------------------------------------- /src/multi_link_core.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Kristian Evensen 3 | * 4 | * This file is part of Multi Network Manager (MNM). MNM is free software: you 5 | * can redistribute it and/or modify it under the terms of the Lesser GNU 6 | * General Public License as published by the Free Software Foundation, either 7 | * version 3 of the License, or (at your option) any later version. 8 | * 9 | * MNM is distributed in the hope that it will be useful, but WITHOUT ANY 10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License along with 14 | * Network Device Listener. If not, see http://www.gnu.org/licenses/. 15 | */ 16 | 17 | #ifndef MULTI_LINK_CORE_H 18 | #define MULTI_LINK_CORE_H 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include "multi_shared.h" 28 | #include "multi_link_shared.h" 29 | 30 | /* Reason for making these global, is that they are accsessed several times in 31 | * the same "class" */ 32 | //The reason I need three sockets is that the set-function sometimes causes 33 | //replies (when for example routes do not exist). This is why I cant use 34 | //request, then it goes crazy because of sequence numbers. Could be solved by 35 | //non-blocking 36 | 37 | //libmnl structure, new netlink communication point, used for requests 38 | struct mnl_socket *multi_link_nl_request; 39 | //libmnl structure used only for events 40 | struct mnl_socket *multi_link_nl_event; 41 | //limnl structure used only for setting parameters 42 | struct mnl_socket *multi_link_nl_set; 43 | //The link module's list of all links 44 | LIST_HEAD(multi_link_links_head, multi_link_info) multi_link_links_2; 45 | uint32_t multi_link_num_links; 46 | 47 | //The pipe used for communication between dhcp and link module 48 | int32_t multi_link_dhcp_pipes[2]; 49 | 50 | void *multi_link_init(void *arg); 51 | //This is also needed by filter 52 | struct multi_link_info *multi_link_create_new_link(uint8_t* dev_name, 53 | uint32_t metric); 54 | //Generic filter algorithm 55 | int32_t multi_link_filter(uint32_t seq, mnl_cb_t cb, void *arg); 56 | #endif 57 | -------------------------------------------------------------------------------- /src/multi_link_filter.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Kristian Evensen 3 | * 4 | * This file is part of Multi Network Manager (MNM). MNM is free software: you 5 | * can redistribute it and/or modify it under the terms of the Lesser GNU 6 | * General Public License as published by the Free Software Foundation, either 7 | * version 3 of the License, or (at your option) any later version. 8 | * 9 | * MNM is distributed in the hope that it will be useful, but WITHOUT ANY 10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License along with 14 | * Network Device Listener. If not, see http://www.gnu.org/licenses/. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include "multi_shared.h" 30 | #include "multi_link_core.h" 31 | #include "multi_link_filter.h" 32 | #include "multi_link_shared.h" 33 | #include "multi_link_netlink.h" 34 | 35 | #include "multi_macros.h" 36 | #include "multi_cmp.h" 37 | 38 | extern struct multi_link_links_head multi_link_links_2; 39 | extern uint32_t multi_link_num_links; 40 | extern struct multi_link_info *multi_link_create_new_link(uint8_t* dev_name, 41 | uint32_t metric); 42 | 43 | //Read the documentation, this basically does what the old parse does (put in 44 | //tb), but is is much more flexible 45 | int32_t multi_link_fill_rtattr(const struct nlattr *attr, void *data){ 46 | //TB is an array, remember that 47 | const struct nlattr **tb = data; 48 | int32_t type = mnl_attr_get_type(attr); 49 | 50 | //Could do some validation here, for example 51 | //Any attribute that is after IFLA_MAX is not valid in userspace, ignore 52 | if(mnl_attr_type_valid(attr, IFLA_MAX) <0) 53 | return MNL_CB_OK; 54 | 55 | tb[type] = attr; 56 | return MNL_CB_OK; 57 | 58 | } 59 | 60 | /* Check if a link is WLAN, and if so, if it is master/monitor (ignore if that 61 | * is the case). Returns the mode. */ 62 | uint8_t multi_link_check_wlan_mode(uint8_t *dev_name){ 63 | int32_t wlan_sock = 0; //Socket for communicating with iwlib 64 | struct wireless_config wcfg; //Malloc this one? 65 | int32_t retval = 0; 66 | 67 | if((wlan_sock = iw_sockets_open()) > 0) 68 | //This can be optimised, MODE is just a normal ioctl 69 | if(!iw_get_basic_config(wlan_sock, (char*) dev_name, &wcfg)){ 70 | if(wcfg.mode == 3 || wcfg.mode == 6) 71 | return wcfg.mode; 72 | } else 73 | close(wlan_sock); 74 | 75 | return 0; 76 | } 77 | 78 | int32_t multi_link_filter_links(const struct nlmsghdr *nlh, void *data){ 79 | //nlattr is the generic form of rtattr 80 | struct nlattr *tb[IFLA_MAX + 1] = {}; 81 | struct ifinfomsg *ifi = mnl_nlmsg_get_payload(nlh); 82 | uint8_t *devname; 83 | struct multi_link_info *li; 84 | struct multi_link_info_static *li_static = NULL; 85 | uint8_t wireless_mode = 0; 86 | 87 | mnl_attr_parse(nlh, sizeof(*ifi), multi_link_fill_rtattr, tb); 88 | 89 | if (!tb[IFLA_IFNAME]) { 90 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Missing interface name\n"); 91 | return MNL_CB_OK; 92 | } 93 | 94 | devname = (uint8_t*) mnl_attr_get_str(tb[IFLA_IFNAME]); 95 | 96 | if (!strncmp(devname, "veth", 4) || 97 | !strncmp(devname, "ifb", 3) || 98 | ifi->ifi_type == ARPHRD_VOID || 99 | (ifi->ifi_type == ARPHRD_NONE && strncmp(devname, "wwan", 4) && strncmp(devname, "nlw_1", 4)) || 100 | ifi->ifi_type == ARPHRD_TUNNEL || 101 | ifi->ifi_flags & IFF_LOOPBACK) 102 | return MNL_CB_OK; 103 | 104 | if((wireless_mode = multi_link_check_wlan_mode(devname))) { 105 | if (wireless_mode == 6) { 106 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Interface %s is wireless monitor, " 107 | "ignoring\n", devname); 108 | return MNL_CB_OK; 109 | } 110 | } 111 | 112 | if (!tb[IFLA_OPERSTATE]) 113 | return MNL_CB_OK; 114 | 115 | //Interface is up, do normal operation 116 | //Last one is for interfaces that are UP, but not running (for example 117 | //no LAN cable) 118 | TAILQ_FIND_CUSTOM(li_static, &multi_shared_static_links, 119 | list_ptr, devname, multi_cmp_devname); 120 | 121 | if(ifi->ifi_flags & IFF_RUNNING || ((ifi->ifi_flags & IFF_UP) && 122 | li_static)){ 123 | //TODO: Assumes that there is initially always room for every link 124 | if (li_static != NULL) { 125 | if(li_static->proto == PROTO_IGNORE){ 126 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Ignoring %s (idx %d) \n", 127 | devname, ifi->ifi_index); 128 | return MNL_CB_OK; 129 | } else { 130 | li = multi_link_create_new_link(devname, li_static->metric); 131 | } 132 | } else { 133 | /* Allocate a new link, add to list and start DHCP */ 134 | li = multi_link_create_new_link(devname, 0); 135 | } 136 | 137 | if (!li) { 138 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Could not create link object\n"); 139 | return MNL_CB_OK; 140 | } 141 | 142 | /* If link exists in static link list, set link to GOT_STATIC */ 143 | if (li_static != NULL && li_static->proto == PROTO_STATIC) { 144 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Link %s assigned static IP\n", devname); 145 | 146 | //I will only set IP, when interface is only up. 147 | if (ifi->ifi_flags & IFF_RUNNING) { 148 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Link %s is RUNNING\n", devname); 149 | li->state = GOT_IP_STATIC; 150 | } else if (ifi->ifi_flags & IFF_UP) { 151 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Link %s is UP\n", devname); 152 | li->state = GOT_IP_STATIC_UP; 153 | } 154 | 155 | li->cfg = li_static->cfg_static; 156 | } else if (ifi->ifi_type == ARPHRD_PPP) { 157 | /* PPP will be dealt with separatley, since they get the IP 158 | * remotely by themself */ 159 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Link %s is PPP!\n", devname); 160 | li->state = LINK_DOWN_PPP; 161 | } else if (wireless_mode == 3) { 162 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Link %s is wireless access point\n", 163 | devname); 164 | li->state = LINK_DOWN_AP; 165 | } else { 166 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Found link %s\n", devname); 167 | } 168 | 169 | //The order in which links are stored in this list is not important 170 | LIST_INSERT_HEAD(&multi_link_links_2, li, next); 171 | ++multi_link_num_links; 172 | } 173 | 174 | return MNL_CB_OK; 175 | } 176 | 177 | int32_t multi_link_filter_ipaddr(const struct nlmsghdr *nlh, void *data){ 178 | struct ip_info *ip_info = (struct ip_info *) data; 179 | struct ifaddrmsg *ifa = mnl_nlmsg_get_payload(nlh); 180 | struct nlattr *tb[IFLA_MAX + 1] = {}; 181 | struct filter_msg *msg; 182 | struct multi_link_info *li = NULL; 183 | struct multi_link_info_static *li_static = NULL; 184 | char devname[IF_NAMESIZE+1] = {0}; 185 | 186 | if (if_indextoname(ifa->ifa_index, devname)) 187 | TAILQ_FIND_CUSTOM(li_static, &multi_shared_static_links, list_ptr, 188 | devname, multi_cmp_devname); 189 | 190 | if (li_static && li_static->proto == PROTO_IGNORE) 191 | return MNL_CB_OK; 192 | 193 | //The reason I need to check in multi_link_links is interfaces that are 194 | //ignored, or that have come up after I dumped the interface info. The first 195 | //case interfaces should be ignored, while the second case interfaces will 196 | //be seen later 197 | LIST_FIND_CUSTOM(li, &multi_link_links_2, next, ifa, multi_cmp_ifidx_flush); 198 | 199 | if(li){ 200 | //Copy the nlmsg, as I will recycle it later when I delete everything! 201 | msg = (struct filter_msg*) malloc(nlh->nlmsg_len + 202 | sizeof(TAILQ_ENTRY(filter_msg))); 203 | memcpy(&(msg->nlh), nlh, nlh->nlmsg_len); 204 | TAILQ_INSERT_TAIL(&(ip_info->ip_addr_n), msg, list_ptr); 205 | 206 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Deleting address for interface %u\n", 207 | ifa->ifa_index); 208 | } 209 | 210 | return MNL_CB_OK; 211 | } 212 | 213 | /* Update the provide PPP link with information about the interface */ 214 | int32_t multi_link_filter_ppp(const struct nlmsghdr *nlh, void *data){ 215 | struct multi_link_info *li = (struct multi_link_info *) data; 216 | struct nlattr *tb[IFLA_MAX + 1] = {}; 217 | struct ifaddrmsg *ifa = mnl_nlmsg_get_payload(nlh); 218 | struct sockaddr_in sa; 219 | uint8_t addr_buf[INET_ADDRSTRLEN]; 220 | 221 | if(ifa->ifa_index != li->ifi_idx) 222 | return MNL_CB_OK; 223 | 224 | mnl_attr_parse(nlh, sizeof(*ifa), multi_link_fill_rtattr, tb); 225 | 226 | if(tb[IFA_LOCAL] && tb[IFA_ADDRESS]){ 227 | li->cfg.address.s_addr = mnl_attr_get_u32(tb[IFA_LOCAL]); 228 | li->cfg.broadcast.s_addr = mnl_attr_get_u32(tb[IFA_ADDRESS]); 229 | li->cfg.gateway.s_addr = 0; 230 | inet_pton(AF_INET, "255.255.255.255", &(li->cfg.netmask)); 231 | li->state = GOT_IP_PPP; 232 | 233 | sa.sin_family = AF_INET; 234 | sa.sin_addr = li->cfg.address; 235 | inet_ntop(AF_INET, &(sa.sin_addr), (char*) addr_buf, INET_ADDRSTRLEN); 236 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Local address: %s \n", addr_buf); 237 | 238 | sa.sin_family = AF_INET; 239 | sa.sin_addr = li->cfg.broadcast; 240 | inet_ntop(AF_INET, &(sa.sin_addr), (char*) addr_buf, INET_ADDRSTRLEN); 241 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Remote address: %s\n", addr_buf); 242 | } 243 | 244 | return MNL_CB_OK; 245 | } 246 | 247 | /* Update the provide PPP link with information about the interface */ 248 | int32_t multi_link_filter_ap(const struct nlmsghdr *nlh, void *data){ 249 | struct multi_link_info *li = (struct multi_link_info *) data; 250 | struct nlattr *tb[IFLA_MAX + 1] = {}; 251 | struct ifaddrmsg *ifa = mnl_nlmsg_get_payload(nlh); 252 | struct sockaddr_in sa; 253 | uint8_t addr_buf[INET_ADDRSTRLEN]; 254 | uint32_t mask = 0; 255 | 256 | //This ifa_scope is just a hack, check in more detail what happens when I 257 | //create an AP 258 | if(ifa->ifa_index != li->ifi_idx) 259 | return MNL_CB_OK; 260 | 261 | //Access point is also configured with an IPv6 route, not interested in that 262 | //now 263 | if(ifa->ifa_family == AF_INET6) 264 | return MNL_CB_OK; 265 | 266 | mnl_attr_parse(nlh, sizeof(*ifa), multi_link_fill_rtattr, tb); 267 | 268 | if(tb[IFA_ADDRESS]){ 269 | li->cfg.address.s_addr = mnl_attr_get_u32(tb[IFA_ADDRESS]); 270 | 271 | //Logic is simple. Set the first bit, movie it the number of 0's to the 272 | //left, subtract 1 (so that all lesser bits are 1) and flip 273 | mask = htonl(~((1 << (32 - ifa->ifa_prefixlen)) - 1)); 274 | li->cfg.netmask.s_addr = mask; 275 | 276 | li->state = GOT_IP_AP; 277 | 278 | sa.sin_family = AF_INET; 279 | sa.sin_addr = li->cfg.address; 280 | inet_ntop(AF_INET, &(sa.sin_addr), (char*) addr_buf, INET_ADDRSTRLEN); 281 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Local address: %s \n", addr_buf); 282 | 283 | sa.sin_family = AF_INET; 284 | sa.sin_addr = li->cfg.netmask; 285 | inet_ntop(AF_INET, &(sa.sin_addr), (char*) addr_buf, INET_ADDRSTRLEN); 286 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Netmask: %s\n", addr_buf); 287 | } 288 | 289 | return MNL_CB_OK; 290 | } 291 | 292 | /* Get the ip rules that belong to the current interfaces */ 293 | int32_t multi_link_filter_iprules(const struct nlmsghdr *nlh, void *data){ 294 | struct ip_info *ip_info = (struct ip_info *) data; 295 | struct rtmsg *rt = mnl_nlmsg_get_payload(nlh); 296 | struct nlattr *tb[FRA_MAX + 1] = {}; 297 | char *iface_name = NULL; 298 | struct filter_msg *msg; 299 | uint32_t fra_priority = 0; 300 | 301 | mnl_attr_parse(nlh, sizeof(*rt), multi_link_fill_rtattr, tb); 302 | 303 | if(!tb[FRA_PRIORITY]) 304 | return MNL_CB_OK; 305 | 306 | fra_priority = mnl_attr_get_u32(tb[FRA_PRIORITY]); 307 | 308 | if (fra_priority != ADDR_RULE_PRIO && 309 | fra_priority != NW_RULE_PRIO && 310 | (fra_priority < (DEF_RULE_PRIO + 1) || fra_priority > DEF_RULE_MAX)) 311 | return MNL_CB_OK; 312 | 313 | //The last part of this check is not perfect, but it works for now. Will 314 | //break when someone adds a rule with a larger priority 315 | if (fra_priority != ADDR_RULE_PRIO && fra_priority != NW_RULE_PRIO && 316 | fra_priority <= DEF_RULE_PRIO) 317 | return MNL_CB_OK; 318 | 319 | //TODO: Add a check for interface here as well, do our best not to do 320 | //anything with interfaces that should be ignored? 321 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Added rule with id %u to flush list\n", 322 | fra_priority); 323 | 324 | /* Add the rule nlmsg to list */ 325 | msg = (struct filter_msg*) malloc(nlh->nlmsg_len + 326 | sizeof(TAILQ_ENTRY(filter_msg))); 327 | memcpy(&(msg->nlh), nlh, nlh->nlmsg_len); 328 | TAILQ_INSERT_TAIL(&(ip_info->ip_rules_n), msg, list_ptr); 329 | 330 | return MNL_CB_OK; 331 | } 332 | 333 | static uint8_t multi_link_filter_cmp_def_rule( 334 | struct multi_link_filter_iprule *filter_iprule, 335 | uint32_t prio) 336 | { 337 | struct multi_link_info *li_itr = multi_link_links_2.lh_first; 338 | 339 | //Rules are returned in order, so this will take care of any duplicate 340 | //rules. We should never have any 91000 + X rules 341 | //pointing to the same table 342 | if (prio == filter_iprule->last_prio) 343 | return 1; 344 | 345 | filter_iprule->last_prio = prio; 346 | 347 | //In case we have missed a dellink, check if there is a network with 348 | //matching metric. We don't need to check for multiple entries with same 349 | //metric, that is taken care of by the previous check 350 | while (li_itr != NULL) { 351 | if (li_itr->metric == prio) 352 | break; 353 | 354 | li_itr = li_itr->next.le_next; 355 | } 356 | 357 | return li_itr == NULL; 358 | } 359 | 360 | static uint8_t multi_link_filter_cmp_nw_addr_rule(struct nlattr *tb[], 361 | struct multi_link_info *li) 362 | { 363 | uint32_t rule_addr, target_table; 364 | struct multi_link_info *li_itr = multi_link_links_2.lh_first; 365 | 366 | if ((!tb[FRA_SRC] && !tb[FRA_DST]) || (tb[FRA_SRC] && tb[FRA_DST])) 367 | return 0; 368 | 369 | if (tb[FRA_SRC]) 370 | rule_addr = mnl_attr_get_u32(tb[FRA_SRC]); 371 | else if(tb[FRA_DST]) 372 | rule_addr = mnl_attr_get_u32(tb[FRA_DST]); 373 | 374 | if (rule_addr != li->cfg.address.s_addr) 375 | return 0; 376 | 377 | target_table = mnl_attr_get_u32(tb[FRA_TABLE]); 378 | 379 | //Need to check if there exists an interface with this IP using this table 380 | while (li_itr != NULL) { 381 | if (li != li_itr && 382 | li_itr->cfg.address.s_addr == rule_addr && 383 | li_itr->metric == target_table) 384 | break; 385 | 386 | li_itr = li_itr->next.le_next; 387 | } 388 | 389 | if (li_itr != NULL) 390 | return 0; 391 | else 392 | return 1; 393 | } 394 | 395 | //Create a list of rules matching the given source address 396 | int32_t multi_link_filter_iprules_addr(const struct nlmsghdr *nlh, void *data) 397 | { 398 | struct ip_info *ip_info = (struct ip_info *) data; 399 | struct rtmsg *rt = mnl_nlmsg_get_payload(nlh); 400 | struct nlattr *tb[FRA_MAX + 1] = {}; 401 | char *iface_name = NULL; 402 | struct filter_msg *msg; 403 | uint32_t target_table, prio; 404 | struct multi_link_filter_iprule *filter_iprule = ip_info->data; 405 | struct multi_link_info *li = filter_iprule->li, *li_itr; 406 | uint8_t should_delete = 0; 407 | 408 | mnl_attr_parse(nlh, sizeof(*rt), multi_link_fill_rtattr, tb); 409 | 410 | if (!tb[FRA_PRIORITY] || !tb[FRA_TABLE]) 411 | return MNL_CB_OK; 412 | 413 | prio = mnl_attr_get_u32(tb[FRA_PRIORITY]); 414 | 415 | if (prio != ADDR_RULE_PRIO && prio != NW_RULE_PRIO && 416 | (prio < (DEF_RULE_PRIO + 1) || prio > DEF_RULE_MAX)) 417 | return MNL_CB_OK; 418 | 419 | if (prio > (DEF_RULE_PRIO + 1) && prio <= DEF_RULE_MAX) 420 | should_delete = multi_link_filter_cmp_def_rule(filter_iprule, prio); 421 | else 422 | should_delete = multi_link_filter_cmp_nw_addr_rule(tb, li); 423 | 424 | if (!should_delete) { 425 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Ignore (sanity) rule pref %u table %u\n", 426 | mnl_attr_get_u32(tb[FRA_PRIORITY]), target_table); 427 | return MNL_CB_OK; 428 | } 429 | 430 | //Add check for other IPs 431 | msg = (struct filter_msg*) malloc(nlh->nlmsg_len + 432 | sizeof(TAILQ_ENTRY(filter_msg))); 433 | 434 | if (msg == NULL) { 435 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Can't allocate memory for rule message\n"); 436 | return MNL_CB_ERROR; 437 | } 438 | 439 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Delete (sanity) rule pref %u\n", 440 | prio); 441 | 442 | memcpy(&(msg->nlh), nlh, nlh->nlmsg_len); 443 | TAILQ_INSERT_TAIL(&(ip_info->ip_rules_n), msg, list_ptr); 444 | return MNL_CB_OK; 445 | } 446 | 447 | int32_t multi_link_filter_iproutes(const struct nlmsghdr *nlh, void *data){ 448 | struct ip_info *ip_info = (struct ip_info *) data; 449 | struct rtmsg *table_i = mnl_nlmsg_get_payload(nlh); 450 | struct nlattr *tb[IFLA_MAX + 1] = {}; 451 | uint32_t ifiIdx = 0; 452 | struct filter_msg *msg; 453 | struct multi_link_info *li = NULL; 454 | 455 | //Ignore table 255 (local). It is updated automatically as IPs are 456 | //added/deleted. This was the cause of the PPP bug, the IP was removed from 457 | //the local table and the kernel did not know what to do! The IP and, 458 | //thereby, implicitly the local table is managed by removing/adding IP 459 | //adresses. 460 | //Also, multi will only use tables 1-32, so stay away from tables other than 461 | //those (now that we anyway dont add routes to default table) 462 | if(table_i->rtm_table == 255 || table_i->rtm_table > MAX_NUM_LINKS) 463 | return MNL_CB_OK; 464 | 465 | mnl_attr_parse(nlh, sizeof(*table_i), multi_link_fill_rtattr, tb); 466 | 467 | if(tb[RTA_OIF]){ 468 | //Check for ignore. I have already fetched the list of all interface, so 469 | //any interface NOT on this list is either specified as ignore, or have 470 | //come up after boot and will be ignored 471 | ifiIdx = mnl_attr_get_u32(tb[RTA_OIF]); 472 | LIST_FIND_CUSTOM(li, &multi_link_links_2, next, &ifiIdx, multi_cmp_ifidx); 473 | 474 | if(li == NULL){ 475 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Not deleting route for idx %u\n", 476 | ifiIdx); 477 | return MNL_CB_OK; 478 | } else 479 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Deleting route for idx %u\n", ifiIdx); 480 | 481 | //Clear out the whole routing table, multi will control everything! 482 | msg = (struct filter_msg*) malloc(nlh->nlmsg_len + 483 | sizeof(TAILQ_ENTRY(filter_msg))); 484 | memcpy(&(msg->nlh), nlh, nlh->nlmsg_len); 485 | TAILQ_INSERT_TAIL(&(ip_info->ip_routes_n), msg, list_ptr); 486 | } 487 | 488 | return MNL_CB_OK; 489 | } 490 | 491 | static void multi_link_free_ip_info_list(struct filter_list *list){ 492 | struct filter_msg *msg; 493 | while(list->tqh_first != NULL){ 494 | msg = (struct filter_msg*) list->tqh_first; 495 | TAILQ_REMOVE(list, list->tqh_first, list_ptr); 496 | free(msg); 497 | } 498 | } 499 | 500 | /* Free the memory used by the ip_info struct */ 501 | void multi_link_free_ip_info(struct ip_info *ip_info){ 502 | multi_link_free_ip_info_list(&(ip_info->ip_addr_n)); 503 | multi_link_free_ip_info_list(&(ip_info->ip_rules_n)); 504 | multi_link_free_ip_info_list(&(ip_info->ip_routes_n)); 505 | } 506 | 507 | -------------------------------------------------------------------------------- /src/multi_link_filter.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Kristian Evensen 3 | * 4 | * This file is part of Multi Network Manager (MNM). MNM is free software: you 5 | * can redistribute it and/or modify it under the terms of the Lesser GNU 6 | * General Public License as published by the Free Software Foundation, either 7 | * version 3 of the License, or (at your option) any later version. 8 | * 9 | * MNM is distributed in the hope that it will be useful, but WITHOUT ANY 10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License along with 14 | * Network Device Listener. If not, see http://www.gnu.org/licenses/. 15 | */ 16 | 17 | #ifndef MULTI_LINK_FILTER_H 18 | #define MULTI_LINK_FILTER_H 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | struct filter_msg{ 26 | //TODO: Union with nlh and uint32 for the address 27 | TAILQ_ENTRY(filter_msg) list_ptr; 28 | struct nlmsghdr nlh; 29 | }; 30 | 31 | TAILQ_HEAD(filter_list, filter_msg); 32 | 33 | /* Helper struct to keep the different lists of information needed to 34 | * configure a system where interfaces are already up */ 35 | //TODO: Use pointers instead? 36 | struct ip_info{ 37 | struct filter_list ip_addr_n; //The nlmsgs, will be used to delete ip addresses 38 | struct filter_list ip_rules_n; //The netlink messages containing the rules 39 | struct filter_list ip_routes_n; //The table ID 40 | void *data; //Pointer that can be used to store private data 41 | }; 42 | 43 | //Helper function for filling in rtattr 44 | int32_t multi_link_fill_rtattr(const struct nlattr *attr, void *data); 45 | uint8_t multi_link_check_wlan_mode(uint8_t *dev_name); 46 | int32_t multi_link_filter_links(const struct nlmsghdr *nlh, void *data); 47 | int32_t multi_link_filter_ipaddr(const struct nlmsghdr *nlh, void *data); 48 | int32_t multi_link_filter_iprules(const struct nlmsghdr *nlh, void *data); 49 | int32_t multi_link_filter_iprules_addr(const struct nlmsghdr *nlh, void *data); 50 | int32_t multi_link_filter_iproutes(const struct nlmsghdr *nlh, void *arg); 51 | int32_t multi_link_filter_ppp(const struct nlmsghdr *nlh, void *data); 52 | int32_t multi_link_filter_ap(const struct nlmsghdr *nlh, void *data); 53 | void multi_link_free_ip_info(struct ip_info *ip_info); 54 | 55 | #endif 56 | -------------------------------------------------------------------------------- /src/multi_link_netlink.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Kristian Evensen 3 | * 4 | * This file is part of Multi Network Manager (MNM). MNM is free software: you 5 | * can redistribute it and/or modify it under the terms of the Lesser GNU 6 | * General Public License as published by the Free Software Foundation, either 7 | * version 3 of the License, or (at your option) any later version. 8 | * 9 | * MNM is distributed in the hope that it will be useful, but WITHOUT ANY 10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License along with 14 | * Network Device Listener. If not, see http://www.gnu.org/licenses/. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include //The rules-thing 24 | #include 25 | #include 26 | 27 | #include "multi_link_core.h" 28 | #include "multi_link_netlink.h" 29 | #include "multi_link_shared.h" 30 | #include "multi_link_filter.h" 31 | 32 | extern struct rtnl_handle multi_link_rth; 33 | //multi_link_filter 34 | extern int32_t multi_link_filter_ppp(const struct nlmsghdr *nlh, void *data); 35 | extern int32_t multi_link_filter_ap(const struct nlmsghdr *nlh, void *data); 36 | //multi_link_core 37 | extern int32_t multi_link_filter(uint32_t seq, mnl_cb_t cb, void *arg); 38 | 39 | /* Add/delete ip rule */ 40 | static int32_t multi_link_modify_rule(uint32_t msg_type, uint32_t flags, 41 | uint32_t table_id, struct multi_link_info *li, uint8_t addr_len, 42 | uint8_t dir, uint32_t prio, const char *ifname){ 43 | uint8_t buf[MNL_SOCKET_BUFFER_SIZE]; 44 | struct nlmsghdr *nlh; 45 | struct rtmsg *rt; 46 | 47 | memset(buf, 0, MNL_SOCKET_BUFFER_SIZE); 48 | 49 | nlh = mnl_nlmsg_put_header(buf); 50 | nlh->nlmsg_type = msg_type; 51 | nlh->nlmsg_flags = NLM_F_REQUEST | flags; 52 | nlh->nlmsg_seq = 0; 53 | 54 | rt = mnl_nlmsg_put_extra_header(nlh, sizeof(struct rtmsg)); 55 | rt->rtm_family = AF_INET; 56 | rt->rtm_dst_len = 0; 57 | rt->rtm_table = table_id; //The table to perform the lookup in 58 | rt->rtm_protocol = RTPROT_BOOT; 59 | rt->rtm_scope = RT_SCOPE_UNIVERSE; 60 | rt->rtm_type = RTN_UNICAST; 61 | 62 | mnl_attr_put_u32(nlh, FRA_PRIORITY, prio); 63 | 64 | if (dir == FRA_SRC) 65 | rt->rtm_src_len = addr_len; 66 | else if (dir == FRA_DST) 67 | rt->rtm_dst_len = addr_len; 68 | 69 | if (rt->rtm_src_len || rt->rtm_dst_len) 70 | mnl_attr_put_u32(nlh, dir, li->cfg.address.s_addr); 71 | 72 | if (ifname) 73 | mnl_attr_put_strz(nlh, FRA_IFNAME, ifname); 74 | 75 | if(mnl_socket_sendto(multi_link_nl_set, nlh, nlh->nlmsg_len) < 0){ 76 | MULTI_DEBUG_PRINT_SYSLOG(stderr,"Could not send rule to kernel " 77 | "(can be ignored if caused by an interface that went down, " 78 | "iface idx %u)\n", li->ifi_idx); 79 | return -1; 80 | } 81 | 82 | return 0; 83 | 84 | } 85 | 86 | /* Add/delete gateway */ 87 | static int32_t multi_link_modify_gateway(uint32_t msg_type, uint32_t flags, 88 | uint32_t table_id, struct multi_link_info *li, uint32_t metric){ 89 | uint8_t buf[MNL_SOCKET_BUFFER_SIZE]; 90 | struct nlmsghdr *nlh; 91 | struct rtmsg *rt; 92 | 93 | nlh = mnl_nlmsg_put_header(buf); 94 | nlh->nlmsg_type = msg_type; 95 | nlh->nlmsg_flags = NLM_F_REQUEST | flags; 96 | nlh->nlmsg_seq = 0; 97 | 98 | rt = mnl_nlmsg_put_extra_header(nlh, sizeof(struct rtmsg)); 99 | rt->rtm_family = AF_INET; 100 | //There is no destination (the destination is global, i.e. netmask 0) 101 | rt->rtm_dst_len = 0; 102 | rt->rtm_table = table_id; 103 | rt->rtm_protocol = RTPROT_UNSPEC; 104 | 105 | /* This is all copied from iproute */ 106 | if(msg_type != RTM_DELROUTE){ 107 | rt->rtm_scope = RT_SCOPE_UNIVERSE; 108 | rt->rtm_type = RTN_UNICAST; 109 | rt->rtm_protocol = RTPROT_BOOT; 110 | } else 111 | rt->rtm_scope = RT_SCOPE_NOWHERE; 112 | 113 | if(li->cfg.gateway.s_addr > 0) 114 | mnl_attr_put_u32(nlh, RTA_GATEWAY, li->cfg.gateway.s_addr); 115 | 116 | mnl_attr_put_u32(nlh, RTA_PREFSRC, li->cfg.address.s_addr); 117 | mnl_attr_put_u32(nlh, RTA_OIF, li->ifi_idx); 118 | 119 | if(metric) 120 | mnl_attr_put_u32(nlh, RTA_PRIORITY, metric); 121 | 122 | if(mnl_socket_sendto(multi_link_nl_set, nlh, nlh->nlmsg_len) < 0){ 123 | MULTI_DEBUG_PRINT_SYSLOG(stderr,"Could not send gateway to kernel " 124 | "(can be ignored if caused by an interface that went down, " 125 | "iface idx %u)\n", li->ifi_idx); 126 | return -1; 127 | } 128 | 129 | return 0; 130 | } 131 | 132 | /* Adds/deletes route. The reason for having metric as a seperate parameter is 133 | * that the value depends on wether this is the private table (0) or not. If the 134 | * route is intended for the private table, then ignore metric */ 135 | static int32_t multi_link_modify_route(uint32_t msg_type, uint32_t flags, 136 | uint32_t table_id, struct multi_link_info *li, uint32_t metric){ 137 | uint8_t buf[MNL_SOCKET_BUFFER_SIZE]; 138 | struct nlmsghdr *nlh; 139 | struct rtmsg *rt; 140 | uint32_t nw_ip = 0; 141 | 142 | //The desired destination IP is store in different places for PPP and 143 | //"normal" interfaces. This is the network route! 144 | if(li->state == GOT_IP_PPP || li->state == LINK_UP_PPP) 145 | nw_ip = li->cfg.broadcast.s_addr; 146 | else 147 | nw_ip = li->cfg.address.s_addr & li->cfg.netmask.s_addr; 148 | 149 | nlh = mnl_nlmsg_put_header(buf); 150 | nlh->nlmsg_type = msg_type; 151 | nlh->nlmsg_flags = NLM_F_REQUEST | flags; 152 | nlh->nlmsg_seq = 0; 153 | 154 | rt = mnl_nlmsg_put_extra_header(nlh, sizeof(struct rtmsg)); 155 | 156 | rt->rtm_scope = RT_SCOPE_NOWHERE; 157 | rt->rtm_type = RTN_UNICAST; 158 | rt->rtm_family = AF_INET; 159 | rt->rtm_dst_len = 32 - (ffs(ntohl(li->cfg.netmask.s_addr)) - 1); 160 | rt->rtm_table = table_id; 161 | 162 | if(msg_type != RTM_DELROUTE){ 163 | rt->rtm_protocol = RTPROT_BOOT; 164 | rt->rtm_scope = RT_SCOPE_LINK; 165 | } 166 | 167 | mnl_attr_put_u32(nlh, RTA_DST, nw_ip); 168 | mnl_attr_put_u32(nlh, RTA_PREFSRC, li->cfg.address.s_addr); 169 | mnl_attr_put_u32(nlh, RTA_OIF, li->ifi_idx); 170 | 171 | if(metric) 172 | mnl_attr_put_u32(nlh, RTA_PRIORITY, metric); 173 | 174 | if(mnl_socket_sendto(multi_link_nl_set, nlh, nlh->nlmsg_len) < 0){ 175 | MULTI_DEBUG_PRINT_SYSLOG(stderr,"Could not send private route to kernel " 176 | "(can be ignored if caused by an interface that went down, " 177 | "iface idx %u)\n", li->ifi_idx); 178 | return -1; 179 | } 180 | 181 | return 0; 182 | } 183 | 184 | /* Adds or deletes the IP of an interface. This function is never called for PPP 185 | * interfaces, thus, there are no special cases. */ 186 | static int32_t multi_link_modify_ip(uint32_t msg_type, uint32_t flags, 187 | struct multi_link_info *li){ 188 | uint8_t buf[MNL_SOCKET_BUFFER_SIZE]; 189 | struct nlmsghdr *nlh; 190 | struct ifaddrmsg *ifa; 191 | 192 | nlh = mnl_nlmsg_put_header(buf); 193 | nlh->nlmsg_type = msg_type; 194 | nlh->nlmsg_flags = NLM_F_REQUEST | flags; 195 | nlh->nlmsg_seq = 0; 196 | 197 | ifa = mnl_nlmsg_put_extra_header(nlh, sizeof(struct ifaddrmsg)); 198 | 199 | /* Fill in info related to address */ 200 | ifa->ifa_family = AF_INET; //Currently only IPv4 201 | 202 | //To avoid this rule that is generated automatically, set bitlen to 32 203 | ifa->ifa_prefixlen = 32 - (ffs(ntohl(li->cfg.netmask.s_addr)) - 1); 204 | //Only reason for changing this is if loopback 205 | ifa->ifa_scope = RT_SCOPE_UNIVERSE; 206 | ifa->ifa_index = li->ifi_idx; 207 | 208 | mnl_attr_put_u32(nlh, IFA_LOCAL, li->cfg.address.s_addr); 209 | mnl_attr_put_u32(nlh, IFA_ADDRESS, li->cfg.address.s_addr); 210 | 211 | if(li->cfg.broadcast.s_addr) 212 | mnl_attr_put_u32(nlh, IFA_BROADCAST, li->cfg.broadcast.s_addr); 213 | 214 | if(mnl_socket_sendto(multi_link_nl_set, nlh, nlh->nlmsg_len) < 0){ 215 | MULTI_DEBUG_PRINT_SYSLOG(stderr,"Could not send IP to kernel (can be ignored " 216 | "if caused by an interface that went down, iface idx %u)\n", 217 | li->ifi_idx); 218 | return -1; 219 | } 220 | 221 | return 0; 222 | } 223 | 224 | void multi_link_get_iface_info(struct multi_link_info *li){ 225 | //MNL_SOCKET_BUFFER_SIZE is 8k, which is the max nlmsg size (see 226 | //linux/netlink.h) 227 | uint8_t buf[MNL_SOCKET_BUFFER_SIZE]; 228 | struct nlmsghdr *nlh; 229 | struct rtgenmsg *rt; 230 | uint32_t seq; 231 | 232 | //It seems like I cant request one interface, has to dump! 233 | ////Play with this later and see what is up 234 | nlh = mnl_nlmsg_put_header(buf); 235 | nlh->nlmsg_type = RTM_GETADDR; 236 | nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; 237 | nlh->nlmsg_seq = seq = time(NULL); //How will this work with event? Send 0? 238 | rt = mnl_nlmsg_put_extra_header(nlh, sizeof(struct rtgenmsg)); 239 | //I need all interfaces, also those without IP (check) 240 | rt->rtgen_family = AF_UNSPEC; 241 | 242 | if(mnl_socket_sendto(multi_link_nl_request, nlh, nlh->nlmsg_len) < 0){ 243 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Cannot request info dump\n"); 244 | return; 245 | } 246 | 247 | if(li->state == LINK_DOWN_PPP) 248 | multi_link_filter(seq, multi_link_filter_ppp, (void*) li); 249 | else if(li->state == LINK_DOWN_AP) 250 | multi_link_filter(seq, multi_link_filter_ap, (void*) li); 251 | } 252 | 253 | /* Function used to remove the information added automatically by pppd */ 254 | /* TODO: Add error codes! */ 255 | void multi_link_remove_ppp(struct multi_link_info *li){ 256 | if(!multi_link_modify_route(RTM_DELROUTE, 0, RT_TABLE_MAIN, li, 0)){ 257 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Removed automatic PPP route!\n"); 258 | } else{ 259 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Failed to remove automatic PPP route!\n"); 260 | return; 261 | } 262 | } 263 | 264 | void multi_link_remove_ap(struct multi_link_info *li){ 265 | if(!multi_link_modify_route(RTM_DELROUTE, 0, RT_TABLE_MAIN, li, 0)){ 266 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Removed automatic AP route!\n"); 267 | } else{ 268 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Failed to remove automatic AP route!\n"); 269 | return; 270 | } 271 | } 272 | 273 | void multi_link_configure_link(struct multi_link_info *li){ 274 | /* Add IP address. PPP/AP has already set the IP of the interface*/ 275 | //It is safe to do this twice in case of GOT_IP_STATIC_UP/GOT_IP_STATIC. An 276 | //interface can only be assigned the same IP address one time. Error will be 277 | //returned the following times. 278 | if(li->state != GOT_IP_PPP && li->state != GOT_IP_AP) 279 | multi_link_modify_ip(RTM_NEWADDR, NLM_F_CREATE | NLM_F_REPLACE, li); 280 | 281 | //Only set IP when link is only up (not running) */ 282 | if(li->state == GOT_IP_STATIC_UP) 283 | return; 284 | 285 | /* Use metric as table ID for now */ 286 | multi_link_modify_route(RTM_NEWROUTE, NLM_F_CREATE | NLM_F_APPEND, 287 | li->metric, li, 0); 288 | 289 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Done setting direct routes (iface %s idx %u)\n", 290 | li->dev_name, li->ifi_idx); 291 | 292 | if(li->state == GOT_IP_AP || (li->state == GOT_IP_STATIC && 293 | !li->cfg.gateway.s_addr)){ 294 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Not setting gateway for %s (idx %u)\n", 295 | li->dev_name, li->ifi_idx); 296 | } else { 297 | multi_link_modify_gateway(RTM_NEWROUTE, NLM_F_CREATE | NLM_F_APPEND, 298 | li->metric, li, 0); 299 | //Delete the route that is automatically added by kernel when we add an 300 | //address with mask < 32 301 | multi_link_modify_route(RTM_DELROUTE, NLM_F_CREATE | NLM_F_APPEND, 302 | RT_TABLE_MAIN, li, 0); 303 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Done setting routes in main table " 304 | "(iface %s idx %u)\n", li->dev_name, li->ifi_idx); 305 | } 306 | 307 | multi_link_modify_rule(RTM_NEWRULE, NLM_F_CREATE | NLM_F_EXCL, li->metric, 308 | li, 32, FRA_SRC, ADDR_RULE_PRIO, NULL); 309 | multi_link_modify_rule(RTM_NEWRULE, NLM_F_CREATE | NLM_F_EXCL, li->metric, 310 | li, 32 - (ffs(ntohl(li->cfg.netmask.s_addr)) - 1), FRA_DST, 311 | NW_RULE_PRIO, NULL); 312 | multi_link_modify_rule(RTM_NEWRULE, NLM_F_CREATE | NLM_F_EXCL, li->metric, 313 | li, 0, 0, DEF_RULE_PRIO + li->metric, "lo"); 314 | 315 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Done adding rule (iface %s idx %u)\n", 316 | li->dev_name, li->ifi_idx); 317 | } 318 | 319 | /* Maybe replace this with a command for flushing */ 320 | void multi_link_remove_link(struct multi_link_info *li){ 321 | multi_link_modify_rule(RTM_DELRULE, 0, li->metric, li, 32, FRA_SRC, 322 | ADDR_RULE_PRIO, NULL); 323 | multi_link_modify_rule(RTM_DELRULE, 0, li->metric, 324 | li, 32 - (ffs(ntohl(li->cfg.netmask.s_addr)) - 1), FRA_DST, 325 | NW_RULE_PRIO, NULL); 326 | multi_link_modify_rule(RTM_DELRULE, NLM_F_CREATE | NLM_F_EXCL, li->metric, 327 | li, 0, 0, DEF_RULE_PRIO + li->metric, "lo"); 328 | 329 | /* This seems to be done by the kernel, but does it depend on something or not? Maybe have a check here */ 330 | if(li->state != GOT_IP_AP) 331 | multi_link_modify_gateway(RTM_DELROUTE, 0, li->metric, li, 0); 332 | 333 | multi_link_modify_route(RTM_DELROUTE, 0, li->metric, li, 0); 334 | 335 | /* Delete IP address */ 336 | if(li->state != GOT_IP_PPP && li->state != LINK_UP_PPP && 337 | li->state != GOT_IP_AP && li->state != LINK_UP_AP && 338 | li->state != LINK_UP_STATIC) 339 | multi_link_modify_ip(RTM_DELADDR, 0, li); 340 | 341 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Cleaned up after %s (iface idx %u)\n", 342 | li->dev_name, li->ifi_idx); 343 | } 344 | -------------------------------------------------------------------------------- /src/multi_link_netlink.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Kristian Evensen 3 | * 4 | * This file is part of Multi Network Manager (MNM). MNM is free software: you 5 | * can redistribute it and/or modify it under the terms of the Lesser GNU 6 | * General Public License as published by the Free Software Foundation, either 7 | * version 3 of the License, or (at your option) any later version. 8 | * 9 | * MNM is distributed in the hope that it will be useful, but WITHOUT ANY 10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License along with 14 | * Network Device Listener. If not, see http://www.gnu.org/licenses/. 15 | */ 16 | 17 | #ifndef MULTI_LINK_NETLINK_H 18 | #define MULTI_LINK_NETLINK_H 19 | 20 | #include "multi_link_shared.h" 21 | 22 | #define ADDR_RULE_PRIO 10000 23 | #define NW_RULE_PRIO 20000 24 | #define DEF_RULE_PRIO 91000 25 | #define DEF_RULE_MAX 91032 26 | 27 | /* Configure IP + routes + rule */ 28 | void multi_link_configure_link(struct multi_link_info *li); 29 | 30 | /* Remote IP + routes + rule */ 31 | void multi_link_remove_link(struct multi_link_info *li); 32 | 33 | /* Remove the info added automatically by pppd/ifconfig (for ap) */ 34 | void multi_link_remove_ppp(struct multi_link_info *li); 35 | extern void multi_link_remove_ap(struct multi_link_info *li); 36 | 37 | /* Get all the registred information about a PPP interface and store it in 38 | * config! */ 39 | void multi_link_get_iface_info(struct multi_link_info *li); 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /src/multi_link_shared.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Kristian Evensen 3 | * 4 | * This file is part of Multi Network Manager (MNM). MNM is free software: you 5 | * can redistribute it and/or modify it under the terms of the Lesser GNU 6 | * General Public License as published by the Free Software Foundation, either 7 | * version 3 of the License, or (at your option) any later version. 8 | * 9 | * MNM is distributed in the hope that it will be useful, but WITHOUT ANY 10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License along with 14 | * Network Device Listener. If not, see http://www.gnu.org/licenses/. 15 | */ 16 | 17 | #ifndef MULTI_LINK_SHARED_H 18 | #define MULTI_LINK_SHARED_H 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include "multi_dhcp_common.h" 26 | #include "multi_common.h" 27 | 28 | #define MAX_PIPE_MSG_LEN 10 //Can be useful later on, if the pipe is used for more useful information 29 | 30 | struct multi_link_info{ 31 | //Used by the DHCP-thread to signal link thread. Used for notification, not 32 | //for identification. DHCP updates the state of this link (if used)! 33 | int32_t write_pipe; 34 | //Store the configuration (new cfg is used when config changes) 35 | struct multi_dhcp_config cfg, new_cfg; 36 | //Name of interface to perform DHCP on 37 | uint8_t dev_name[IFNAMSIZ]; 38 | //Indicates which state the link is in, used by DHCP and Link module to 39 | //decide on action 40 | link_state state; 41 | //Convenience, the interface index is used so many times that it makes 42 | //sense to put it here 43 | uint32_t ifi_idx; 44 | //Routing table metric 45 | uint32_t metric; 46 | //Set for config entries with the metric set. The metric is assumed to be 47 | //persistent 48 | uint8_t keep_metric; 49 | //Used to instruct DHCP client that the IP should be declined 50 | int32_t decline_pipe[2]; 51 | pthread_t dhcp_thread; 52 | //This might not have to be locked at all (can leave with some level of 53 | //instability), but have rwlock for now 54 | pthread_mutex_t state_lock; 55 | 56 | LIST_ENTRY(multi_link_info) next; 57 | }; 58 | 59 | //Struct used to filter iprules, store and check last metric 60 | struct multi_link_filter_iprule { 61 | struct multi_link_info *li; 62 | uint32_t last_prio; 63 | }; 64 | 65 | #endif 66 | -------------------------------------------------------------------------------- /src/multi_macros.h: -------------------------------------------------------------------------------- 1 | #ifndef MULTI_MACROS_H 2 | #define MULTI_MACROS_H 3 | 4 | #include 5 | 6 | //TODO: Implement a prorotype for func 7 | 8 | //var is the value to store the match in (NULL otherwise) 9 | //head is the list 10 | //field is list-pointer 11 | //value is the value to look for 12 | //func is the function to call for each list member, compares 13 | //value and var. Returns 0 if equal, 1 if not 14 | #define TAILQ_FIND_CUSTOM(var, head, field, value, func) do{ \ 15 | TAILQ_FOREACH(var, head, field){ \ 16 | if(!func(var, value)) \ 17 | break; \ 18 | }; \ 19 | } while(0) 20 | 21 | #define LIST_FIND_CUSTOM(var, head, field, value, func) do{ \ 22 | LIST_FOREACH(var, head, field){ \ 23 | if(!func(var, value)) \ 24 | break; \ 25 | }; \ 26 | } while(0) 27 | 28 | //Head is the list head and field the element field. cb is the callback to call 29 | //(accepts two void pointer arguments), var is the element in the list and data 30 | //is user data to pass to callback 31 | #define LIST_FOREACH_CB(head, field, cb, var, data) do{ \ 32 | LIST_FOREACH(var, head, field){ \ 33 | cb(var, data); \ 34 | } \ 35 | } while(0) 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /src/multi_multicast.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Kristian Evensen 3 | * 4 | * This file is part of Multi Network Manager (MNM). MNM is free software: you 5 | * can redistribute it and/or modify it under the terms of the Lesser GNU 6 | * General Public License as published by the Free Software Foundation, either 7 | * version 3 of the License, or (at your option) any later version. 8 | * 9 | * MNM is distributed in the hope that it will be useful, but WITHOUT ANY 10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License along with 14 | * Network Device Listener. If not, see http://www.gnu.org/licenses/. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include "multi_common.h" 30 | #include "multi_core.h" 31 | #include "multi_shared.h" 32 | #include "multi_macros.h" 33 | 34 | extern char *optarg; 35 | extern int32_t opterr; 36 | extern struct multi_config* initialize_config(uint8_t *cfg_file, 37 | uint8_t unique); 38 | extern pthread_t multi_start(struct multi_config *mc); 39 | 40 | struct iface{ 41 | int32_t ifi_idx; 42 | struct nlmsghdr *nlmsg; 43 | LIST_ENTRY(iface) next; 44 | }; 45 | 46 | static LIST_HEAD(iface_head, iface) iface_list; 47 | 48 | //Simple comparison function used to find out if an interface is already in the 49 | //list 50 | static uint8_t multi_mc_cmp_ifi(void *a, void *b){ 51 | struct iface *ifa = (struct iface*) a; 52 | uint32_t *ifi_idx = (uint32_t *) b; 53 | 54 | if(ifa->ifi_idx == *ifi_idx) 55 | return 0; 56 | else 57 | return 1; 58 | } 59 | 60 | void multi_test_visible_loop(struct multi_config *mc){ 61 | uint8_t buf[MAX_BUFSIZE]; 62 | int32_t retval; 63 | int32_t i; 64 | int32_t netlink_sock = 0; 65 | uint32_t ifi_idx = 0; 66 | struct iface *ni = NULL; 67 | 68 | /* Needed to create the netlink messages */ 69 | struct sockaddr_nl src_addr, dest_addr; 70 | struct iovec iov; 71 | struct msghdr msg; 72 | 73 | /* Select is used for easier timeouts */ 74 | fd_set copy, master; 75 | int32_t fdmax; 76 | struct timeval tv; 77 | 78 | memset(buf, 0, MAX_BUFSIZE); 79 | 80 | /* Initialise list */ 81 | LIST_INIT(&iface_list); 82 | 83 | /* Define a private constant later! Needs to be set in netlink.h so that the 84 | * kernel will allow the socket to be created! */ 85 | if((netlink_sock = socket(PF_NETLINK, SOCK_RAW, NETLINK_GENERIC)) < 0){ 86 | perror("Could not create netlink socket: "); 87 | exit(EXIT_FAILURE); 88 | } 89 | 90 | /* These are both constant! */ 91 | memset(&src_addr, 0, sizeof(src_addr)); 92 | memset(&dest_addr, 0, sizeof(dest_addr)); 93 | memset(&msg, 0, sizeof(msg)); 94 | 95 | src_addr.nl_family = AF_NETLINK; 96 | //If PID is set to zero, then the kernel assigns the unique value 97 | src_addr.nl_pid = 0; 98 | //This is the source, it only multicasts, so it does not have to be 99 | //member of any groups! 100 | src_addr.nl_groups = 0; 101 | 102 | if(bind(netlink_sock, (struct sockaddr *) &src_addr, sizeof(src_addr)) < 0){ 103 | perror("Could not bind netlink socket: "); 104 | exit(EXIT_FAILURE); 105 | } 106 | 107 | dest_addr.nl_family = AF_NETLINK; 108 | dest_addr.nl_pid = 0; 109 | dest_addr.nl_groups = 1; 110 | 111 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "Multi manager is ready! Netlink socket %d\n", 112 | netlink_sock); 113 | MULTI_DEBUG_PRINT_SYSLOG(stderr, "M S\n"); 114 | 115 | //Look at the Wikipedia site for iovec and man(7) netlink for examples on 116 | //how to properly parse netlink and have multiple iovec-entries 117 | msg.msg_name = (void *) &dest_addr; //This is the message's destination 118 | msg.msg_namelen = sizeof(dest_addr); 119 | msg.msg_iov = &iov; 120 | msg.msg_iovlen = 1; 121 | 122 | //Initialise everything related to select 123 | FD_ZERO(&master); 124 | FD_ZERO(©); 125 | FD_SET(mc->socket_pipe[0], &master); 126 | fdmax = mc->socket_pipe[0]; 127 | tv.tv_sec = 30; 128 | tv.tv_usec = 0; 129 | 130 | pthread_t multi_thread = multi_start(mc); 131 | 132 | while(1){ 133 | copy = master; 134 | retval = select(fdmax + 1, ©, NULL, NULL, &tv); 135 | 136 | //Repeat every UP message 137 | if(retval == 0){ 138 | for(ni = iface_list.lh_first; ni != NULL; ni = ni->next.le_next){ 139 | iov.iov_base = (void *) ni->nlmsg; 140 | iov.iov_len = ni->nlmsg->nlmsg_len; 141 | sendmsg(netlink_sock, &msg, 0); 142 | } 143 | 144 | tv.tv_sec = 30; 145 | tv.tv_usec = 0; 146 | continue; 147 | } 148 | 149 | memset(buf, 0, MAX_BUFSIZE); 150 | 151 | //Sufficient to just memcpy this one and broadcast the netlink message 152 | retval = read(mc->socket_pipe[0], buf, MAX_BUFSIZE); 153 | 154 | if(retval == -1) 155 | perror("Failed to read from pipe"); 156 | else { 157 | memcpy(&ifi_idx, (buf+1), sizeof(uint32_t)); 158 | 159 | //This check needs to be performed irrespective of if link goes up 160 | //or down. 161 | LIST_FIND_CUSTOM(ni, &iface_list, next, &ifi_idx, 162 | multi_mc_cmp_ifi); 163 | 164 | if(buf[0] == LINK_UP){ 165 | //Sanity check. If the interface is already found, ignore the 166 | //announcement from MULTI. 167 | if(ni) 168 | continue; 169 | 170 | //Create a new iface, buffer the up message and add to list 171 | ni = (struct iface*) malloc(sizeof(struct iface)); 172 | ni->ifi_idx = ifi_idx; 173 | ni->nlmsg = (struct nlmsghdr *) malloc(NLMSG_SPACE(retval)); 174 | memset(ni->nlmsg, 0, NLMSG_SPACE(retval)); 175 | ni->nlmsg->nlmsg_pid = getpid(); 176 | ni->nlmsg->nlmsg_flags = 0; 177 | ni->nlmsg->nlmsg_len = NLMSG_SPACE(retval); 178 | memcpy(NLMSG_DATA(ni->nlmsg), buf, retval); 179 | LIST_INSERT_HEAD(&iface_list, ni, next); 180 | 181 | //Adjust the base pointer of the message and broadcast message 182 | iov.iov_base = (void *) ni->nlmsg; 183 | iov.iov_len = ni->nlmsg->nlmsg_len; 184 | retval = sendmsg(netlink_sock, &msg, 0); 185 | MULTI_DEBUG_PRINT_SYSLOG(stderr,"Broadcasted %d bytes about an UP " 186 | "change in network state\n", retval); 187 | } else { 188 | if(ni){ 189 | //Forward message from MULTI 190 | ni->nlmsg->nlmsg_len = NLMSG_SPACE(retval); 191 | memcpy(NLMSG_DATA(ni->nlmsg), buf, retval); 192 | iov.iov_base = (void *) ni->nlmsg; 193 | iov.iov_len = ni->nlmsg->nlmsg_len; 194 | retval = sendmsg(netlink_sock, &msg, 0); 195 | MULTI_DEBUG_PRINT_SYSLOG(stderr,"Broadcasted %d bytes about a " 196 | "DOWN change in network state\n", retval); 197 | 198 | LIST_REMOVE(ni, next); 199 | free(ni); 200 | } 201 | } 202 | } 203 | } 204 | } 205 | 206 | /* Parse arguments, start multi-thread and get on with life */ 207 | int main(int argc, char *argv[]){ 208 | int32_t c; 209 | char *conf_file = NULL; 210 | char *log_file = NULL; 211 | struct multi_config *mc = NULL; //Do NOT free this struct 212 | uint8_t daemon_mode = 0; 213 | uint8_t unique_mode = 0; 214 | 215 | if(geteuid() != 0){ 216 | fprintf(stderr, "Application MUST be run as root\n"); 217 | exit(EXIT_FAILURE); 218 | } 219 | 220 | /* Supress any error-messages from getopt */ 221 | opterr = 0; 222 | 223 | while((c = getopt(argc, argv, "c:l:du")) != -1){ 224 | switch(c){ 225 | case 'c': 226 | conf_file = optarg; 227 | break; 228 | case 'l': 229 | log_file = optarg; 230 | break; 231 | case'd': 232 | daemon_mode = 1; 233 | break; 234 | case 'u': 235 | unique_mode = 1; 236 | break; 237 | default: 238 | abort(); 239 | } 240 | } 241 | 242 | if((mc = multi_core_initialize_config(conf_file, unique_mode)) == NULL){ 243 | printf("Could not initialize configuration struct\n"); 244 | abort(); 245 | } 246 | 247 | if(daemon_mode){ 248 | if(daemon(0, 0) == -1){ 249 | perror("Could not daemonize MULTI: "); 250 | exit(EXIT_FAILURE); 251 | } 252 | 253 | if (log_file == NULL) { 254 | log_file = "/var/log/multi.log"; 255 | } 256 | 257 | if(freopen(log_file, "a", stderr) == NULL){ 258 | perror("freopen failed: "); 259 | exit(EXIT_FAILURE); 260 | } 261 | } 262 | 263 | multi_test_visible_loop(mc); 264 | } 265 | -------------------------------------------------------------------------------- /src/multi_shared.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Kristian Evensen 3 | * 4 | * This file is part of Multi Network Manager (MNM). MNM is free software: you 5 | * can redistribute it and/or modify it under the terms of the Lesser GNU 6 | * General Public License as published by the Free Software Foundation, either 7 | * version 3 of the License, or (at your option) any later version. 8 | * 9 | * MNM is distributed in the hope that it will be useful, but WITHOUT ANY 10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License along with 14 | * Network Device Listener. If not, see http://www.gnu.org/licenses/. 15 | */ 16 | 17 | #ifndef MULTI_SHARED_H 18 | #define MULTI_SHARED_H 19 | 20 | #include 21 | #include 22 | #include 23 | #include "multi_dhcp_common.h" 24 | 25 | #define MAX_CFG_LEN 256 26 | #define MAX_IP_LEN 16 //Can use inetaddrestrlen or whatever its name is 27 | #define MAX_PORT_LEN 6 28 | #define MAX_NUM_LINKS 32 29 | 30 | #define TRANSPARENT 1 31 | #define VISIBLE 0 32 | 33 | //How IP is acquired. Either static (specified in config) or other (for example 34 | //DHCP). 35 | typedef enum{ 36 | PROTO_STATIC, 37 | PROTO_OTHER, 38 | PROTO_IGNORE, 39 | } multi_proto; 40 | 41 | /* This struct will be filled with info and passed to MULTI */ 42 | struct multi_config{ 43 | uint8_t cfg_file[MAX_CFG_LEN]; //Configuration file for interfaces 44 | int32_t socket_pipe[2]; 45 | uint8_t unique; //Enforce unique IP addresses 46 | }; 47 | 48 | /* Only a simple representation needed to store static links */ 49 | struct multi_link_info_static{ 50 | uint8_t dev_name[IFNAMSIZ]; 51 | struct multi_dhcp_config cfg_static; 52 | uint32_t metric; 53 | multi_proto proto; 54 | 55 | TAILQ_ENTRY(multi_link_info_static) list_ptr; 56 | }; 57 | 58 | //TODO: Naming convention 59 | //List of static interfaces 60 | TAILQ_HEAD(multi_shared_static_links_list, multi_link_info_static) 61 | multi_shared_static_links; 62 | 63 | //Bitset for keeping track of metrics. Must be larger if MAX_NUM_LINKS is 64 | //increased 65 | uint32_t multi_shared_metrics_set; 66 | #endif 67 | --------------------------------------------------------------------------------