├── 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 |
--------------------------------------------------------------------------------