├── .gitignore ├── CMakeLists.txt ├── COPYING ├── README └── src ├── config.c ├── dhcpv4.c ├── dhcpv4.h ├── dhcpv6-ia.c ├── dhcpv6-pxe.c ├── dhcpv6-pxe.h ├── dhcpv6.c ├── dhcpv6.h ├── ndp.c ├── netlink.c ├── odhcpd.c ├── odhcpd.h ├── router.c ├── router.h └── ubus.c /.gitignore: -------------------------------------------------------------------------------- 1 | .project 2 | .cproject 3 | odhcpd 4 | config.log 5 | CMakeCache.txt 6 | CMakeFiles 7 | CPackConfig.cmake 8 | CPackSourceConfig.cmake 9 | _CPack_Packages 10 | Makefile 11 | cmake_install.cmake 12 | install_manifest.txt 13 | *.deb 14 | 15 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | cmake_policy(SET CMP0015 NEW) 3 | 4 | # Project Definition 5 | project(odhcpd C) 6 | 7 | set(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") 8 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g3 -std=gnu99") 9 | 10 | FIND_PATH(ubox_include_dir libubox/uloop.h) 11 | FIND_PATH(libnl-tiny_include_dir netlink-generic.h PATH_SUFFIXES libnl-tiny) 12 | INCLUDE_DIRECTORIES(${ubox_include_dir} ${libnl-tiny_include_dir}) 13 | 14 | FIND_LIBRARY(libnl NAMES nl-tiny) 15 | 16 | add_definitions(-D_GNU_SOURCE -Os -Wall -Werror --std=gnu99) 17 | IF(CMAKE_C_COMPILER_VERSION VERSION_GREATER 6) 18 | add_definitions(-Wextra -Werror=implicit-function-declaration) 19 | add_definitions(-Wformat -Werror=format-security -Werror=format-nonliteral) 20 | ENDIF() 21 | add_definitions(-Wno-unused-parameter -Wmissing-declarations) 22 | 23 | 24 | if (${EXT_CER_ID}) 25 | add_definitions(-DEXT_CER_ID=${EXT_CER_ID}) 26 | endif(${EXT_CER_ID}) 27 | 28 | if(${UBUS}) 29 | add_definitions(-DWITH_UBUS) 30 | set(EXT_SRC ${EXT_SRC} src/ubus.c) 31 | set(EXT_LINK ${EXT_LINK} ubus) 32 | endif(${UBUS}) 33 | 34 | if(${DHCPV4_SUPPORT}) 35 | add_definitions(-DDHCPV4_SUPPORT) 36 | set(EXT_SRC ${EXT_SRC} src/dhcpv4.c) 37 | endif(${DHCPV4_SUPPORT}) 38 | 39 | add_executable(odhcpd src/odhcpd.c src/config.c src/router.c src/dhcpv6.c src/ndp.c src/dhcpv6-ia.c src/dhcpv6-pxe.c src/netlink.c ${EXT_SRC}) 40 | target_link_libraries(odhcpd resolv ubox uci ${libnl} ${EXT_LINK}) 41 | 42 | # Installation 43 | install(TARGETS odhcpd DESTINATION sbin/) 44 | 45 | 46 | # Packaging information 47 | set(CPACK_PACKAGE_VERSION "1") 48 | set(CPACK_PACKAGE_CONTACT "Steven Barth ") 49 | set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "odhcpd") 50 | set(CPACK_GENERATOR "DEB;RPM;STGZ") 51 | set(CPACK_STRIP_FILES true) 52 | 53 | SET(CPACK_DEBIAN_PACKAGE_VERSION ${CPACK_PACKAGE_VERSION}) 54 | set(CPACK_PACKAGE_FILE_NAME "${PROJECT_NAME}_${CPACK_DEBIAN_PACKAGE_VERSION}") 55 | 56 | include(CPack) 57 | 58 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc. 5 | 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Library General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License 307 | along with this program; if not, write to the Free Software 308 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 309 | 310 | 311 | Also add information on how to contact you by electronic and paper mail. 312 | 313 | If the program is interactive, make it output a short notice like this 314 | when it starts in an interactive mode: 315 | 316 | Gnomovision version 69, Copyright (C) year name of author 317 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 318 | This is free software, and you are welcome to redistribute it 319 | under certain conditions; type `show c' for details. 320 | 321 | The hypothetical commands `show w' and `show c' should show the appropriate 322 | parts of the General Public License. Of course, the commands you use may 323 | be called something other than `show w' and `show c'; they could even be 324 | mouse-clicks or menu items--whatever suits your program. 325 | 326 | You should also get your employer (if you work as a programmer) or your 327 | school, if any, to sign a "copyright disclaimer" for the program, if 328 | necessary. Here is a sample; alter the names: 329 | 330 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 331 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 332 | 333 | , 1 April 1989 334 | Ty Coon, President of Vice 335 | 336 | This General Public License does not permit incorporating your program into 337 | proprietary programs. If your program is a subroutine library, you may 338 | consider it more useful to permit linking proprietary applications with the 339 | library. If this is what you want to do, use the GNU Library General 340 | Public License instead of this License. 341 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | odhcpd - Embedded DHCP/DHCPv6/RA Server & Relay 2 | 3 | ** Abstract ** 4 | 5 | odhcpd is a daemon for serving and relaying IP management protocols to 6 | configure clients and downstream routers. It tries to follow the RFC 6204 7 | requirements for IPv6 home routers. 8 | 9 | odhcpd provides server services for DHCP, RA, stateless and stateful DHCPv6, 10 | prefix delegation and can be used to relay RA, DHCPv6 and NDP between routed 11 | (non-bridged) interfaces in case no delegated prefixes are available. 12 | 13 | 14 | ** Features ** 15 | 16 | 1. Router Discovery support (solicitations and advertisements) with 2 modes 17 | server: RD server for slave interfaces 18 | a) automatic detection of prefixes, delegated prefix and default routes, MTU 19 | b) automatic reannouncement when changes to prefixes or routes occur 20 | 21 | relay: RD relay between master and slave interfaces 22 | a) support for rewriting announced DNS-server addresses in relay mode 23 | 24 | 2. DHCPv6-support with 2 modes of operation 25 | server: stateless, stateful and PD-server mode 26 | a) stateless and stateful address assignment 27 | b) prefix delegation support 28 | c) dynamic reconfiguration in case prefixes change 29 | d) hostname detection and hosts-file creation 30 | 31 | relay: mostly standards-compliant DHCPv6-relay 32 | a) support for rewriting announced DNS-server addresses 33 | 34 | 3. DHCPv4-support 35 | server: stateless and stateful mode 36 | 37 | 4. Proxy for Neighbor Discovery messages (solicitations and advertisements) 38 | a) support for auto-learning routes to the local routing table 39 | b) support for marking interfaces "external" not proxying NDP for them 40 | and only serving NDP for DAD and for traffic to the router itself 41 | [Warning: you should provide additional firewall rules for security] 42 | 43 | 5. IPv6 PxE Support. 44 | 45 | ** Compiling ** 46 | 47 | odhcpd uses cmake: 48 | * To prepare a Makefile use: "cmake ." 49 | * To build / install use: "make" / "make install" afterwards. 50 | * To build DEB or RPM packages use: "make package" afterwards. 51 | 52 | 53 | ** Configuration ** 54 | 55 | odhcpd uses a UCI configuration file in /etc/config/dhcp for configuration 56 | and may also receive information from ubus 57 | 58 | 59 | Section of type odhcpd 60 | 61 | Option Type Default Description 62 | legacy bool 0 Enable DHCPv4 if start but 63 | no dhcpv4 option set 64 | maindhcp bool 0 Use odhcpd as the main DHCPv4 65 | service 66 | leasefile string DHCP/v6 lease/hostfile 67 | leasetrigger string Lease trigger script 68 | hostsfile string DHCP/v6 hostfile 69 | loglevel integer 6 Syslog level priority (0-7) 70 | 71 | 72 | Sections of type dhcp (configure DHCP / DHCPv6 / RA / NDP service) 73 | 74 | Option Type Default Description 75 | interface string logical OpenWrt interface 76 | ifname string physical network interface 77 | networkid string same as ifname compat. alias for ifname 78 | master bool 0 is a master interface 79 | for relaying 80 | 81 | ra string disabled Router Advert service 82 | [disabled|server|relay|hybrid] 83 | dhcpv6 string disabled DHCPv6 service 84 | [disabled|server|relay|hybrid] 85 | dhcpv4 string disabled DHCPv4 service 86 | [disabled|server] 87 | ndp string disabled Neighbor Discovery Proxy 88 | [disabled|relay|hybrid] 89 | 90 | dynamicdhcp bool 1 Dynamically create leases 91 | for DHCPv4 and DHCPv6 92 | dhcpv4_forcereconf bool 0 Force reconfiguration by sending 93 | force renew message even if the client 94 | did not include the force renew nonce 95 | capability option (RFC6704) 96 | dhcpv6_assignall bool 1 Assign all viable DHCPv6 addresses 97 | in statefull mode; if disabled 98 | only the DHCPv6 address having the 99 | longest preferred lifetime is assigned 100 | dhcpv6_hostidlength integer 12 Host ID length of dynamically created leases, 101 | allowed values: 12 - 64 (bits). 102 | dhcpv6_na bool 1 DHCPv6 stateful addressing hands out IA_NA - 103 | Internet Address - Network Address 104 | dhcpv6_pd bool 1 DHCPv6 stateful addressing hands out IA_PD - 105 | Internet Address - Prefix Delegation 106 | dhcpv6_pd_min_len integer - Minimum prefix length to delegate with IA_PD 107 | (value is adjusted if needed to be greater 108 | than the interface prefix length). Range [1,62] 109 | router list Routers to announce 110 | accepts IPv4 only 111 | dns list DNS servers to announce 112 | accepts IPv4 and IPv6 113 | dnr list disabled Encrypted DNS servers to announce 114 | [ ...] 115 | dns_service bool 1 Announce the address of interface as DNS service 116 | if the list of dns is empty 117 | domain list Search domains to announce 118 | 119 | leasetime string 12h DHCPv4 address leasetime 120 | start integer 100 DHCPv4 pool start 121 | limit integer 150 DHCPv4 pool size 122 | preferred_lifetime string 7d Value for the preferred lifetime 123 | for a prefix 124 | ra_default integer 0 Override default route 125 | 0: default, 1: ignore no public address, 2: ignore all 126 | ra_flags list other-config List of RA flags to be 127 | advertised in RA messages 128 | [managed-config other-config home-agent none] 129 | ra_slaac bool 1 Announce slaac for a prefix 130 | ra_offlink bool 0 Announce prefixes off-link 131 | ra_preference string medium Route(r) preference 132 | [medium|high|low] 133 | ra_maxinterval integer 600 Maximum time allowed between 134 | sending unsolicited RA 135 | ra_mininterval integer 200 Minimum time allowed between 136 | sending unsolicited RA 137 | ra_lifetime integer 1800 Value to be placed in Router 138 | Lifetime field of RA 139 | ra_useleasetime bool 0 Use configured leasetime as 140 | limit for the preferred and 141 | valid lifetime of a prefix 142 | ra_reachabletime integer 0 Reachable Time in milliseconds to be 143 | advertised in RA messages 144 | ra_retranstime integer 0 Retransmit Time in milliseconds to be 145 | advertised in RA messages 146 | ra_hoplimit integer 0 Current hoplimit to be advertised 147 | in RA messages 148 | ra_mtu integer - MTU to be advertised in 149 | RA messages 150 | ra_dns bool 1 Announce DNS configuration in 151 | RA messages (RFC8106) 152 | ra_pref64 string Announce PREF64 option 153 | [IPv6 prefix] for NAT64 prefix (RFC8781) 154 | ndproxy_routing bool 1 Learn routes from NDP 155 | ndproxy_slave bool 0 NDProxy external slave 156 | prefix_filter string ::/0 Only advertise on-link prefixes within 157 | [IPv6 prefix] the provided IPv6 prefix; others are 158 | filtered out. 159 | ntp list NTP servers to announce 160 | accepts IPv4 and IPv6 161 | 162 | 163 | Sections of type host (static leases) 164 | Option Type Default Description 165 | ip string IP-Address to lease 166 | mac string MAC-address 167 | duid string DUID in base16 168 | hostid string IPv6 host identifier 169 | name string Hostname 170 | leasetime string DHCPv4/v6 leasetime 171 | 172 | Sections of type boot6 173 | Option Type Required Description 174 | url string yes e.g. tftp://[fd11::1]/pxe.efi 175 | arch integer no the arch code. 07 is EFI. 176 | If not present, this boot6 will be the default. 177 | -------------------------------------------------------------------------------- /src/dhcpv4.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2012 Steven Barth 3 | * Copyright (C) 2016 Hans Dedecker 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License version 2 7 | * as published by the Free Software Foundation. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License version 2 for more details. 13 | * 14 | */ 15 | #pragma once 16 | 17 | #define DHCPV4_CLIENT_PORT 68 18 | #define DHCPV4_SERVER_PORT 67 19 | 20 | #define DHCPV4_FLAG_BROADCAST 0x8000 21 | 22 | #define DHCPV4_MIN_PACKET_SIZE 300 23 | 24 | enum dhcpv4_op { 25 | DHCPV4_BOOTREQUEST = 1, 26 | DHCPV4_BOOTREPLY = 2 27 | }; 28 | 29 | enum dhcpv4_msg { 30 | DHCPV4_MSG_DISCOVER = 1, 31 | DHCPV4_MSG_OFFER = 2, 32 | DHCPV4_MSG_REQUEST = 3, 33 | DHCPV4_MSG_DECLINE = 4, 34 | DHCPV4_MSG_ACK = 5, 35 | DHCPV4_MSG_NAK = 6, 36 | DHCPV4_MSG_RELEASE = 7, 37 | DHCPV4_MSG_INFORM = 8, 38 | DHCPV4_MSG_FORCERENEW = 9, 39 | }; 40 | 41 | enum dhcpv4_opt { 42 | DHCPV4_OPT_PAD = 0, 43 | DHCPV4_OPT_NETMASK = 1, 44 | DHCPV4_OPT_ROUTER = 3, 45 | DHCPV4_OPT_DNSSERVER = 6, 46 | DHCPV4_OPT_DOMAIN = 15, 47 | DHCPV4_OPT_MTU = 26, 48 | DHCPV4_OPT_BROADCAST = 28, 49 | DHCPV4_OPT_NTPSERVER = 42, 50 | DHCPV4_OPT_LEASETIME = 51, 51 | DHCPV4_OPT_MESSAGE = 53, 52 | DHCPV4_OPT_SERVERID = 54, 53 | DHCPV4_OPT_REQOPTS = 55, 54 | DHCPV4_OPT_RENEW = 58, 55 | DHCPV4_OPT_REBIND = 59, 56 | DHCPV4_OPT_IPADDRESS = 50, 57 | DHCPV4_OPT_HOSTNAME = 12, 58 | DHCPV4_OPT_REQUEST = 17, 59 | DHCPV4_OPT_USER_CLASS = 77, 60 | DHCPV4_OPT_AUTHENTICATION = 90, 61 | DHCPV4_OPT_SEARCH_DOMAIN = 119, 62 | DHCPV4_OPT_FORCERENEW_NONCE_CAPABLE = 145, 63 | DHCPV4_OPT_DNR = 162, 64 | DHCPV4_OPT_END = 255, 65 | }; 66 | 67 | struct dhcpv4_message { 68 | uint8_t op; 69 | uint8_t htype; 70 | uint8_t hlen; 71 | uint8_t hops; 72 | uint32_t xid; 73 | uint16_t secs; 74 | uint16_t flags; 75 | struct in_addr ciaddr; 76 | struct in_addr yiaddr; 77 | struct in_addr siaddr; 78 | struct in_addr giaddr; 79 | uint8_t chaddr[16]; 80 | char sname[64]; 81 | char file[128]; 82 | uint8_t options[312]; 83 | }; 84 | 85 | struct dhcpv4_auth_forcerenew { 86 | uint8_t protocol; 87 | uint8_t algorithm; 88 | uint8_t rdm; 89 | uint32_t replay[2]; 90 | uint8_t type; 91 | uint8_t key[16]; 92 | } _packed; 93 | 94 | struct dhcpv4_option { 95 | uint8_t type; 96 | uint8_t len; 97 | uint8_t data[]; 98 | }; 99 | 100 | 101 | #define dhcpv4_for_each_option(start, end, opt)\ 102 | for (opt = (struct dhcpv4_option*)(start); \ 103 | &opt[1] <= (struct dhcpv4_option*)(end) && \ 104 | &opt->data[opt->len] <= (end); \ 105 | opt = (struct dhcpv4_option*)&opt->data[opt->len]) 106 | -------------------------------------------------------------------------------- /src/dhcpv6-pxe.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | #include "dhcpv6.h" 7 | #include "dhcpv6-pxe.h" 8 | 9 | struct ipv6_pxe_entry { 10 | struct list_head list; // List head for linking 11 | uint32_t arch; 12 | 13 | // Ready to send 14 | struct __attribute__((packed)) { 15 | uint16_t type; // In network endianess 16 | uint16_t len; // In network endianess, without /0 17 | char payload[]; // Null-terminated here 18 | } bootfile_url; 19 | }; 20 | 21 | static struct ipv6_pxe_entry* ipv6_pxe_default = NULL; 22 | LIST_HEAD(ipv6_pxe_list); 23 | 24 | const struct ipv6_pxe_entry* ipv6_pxe_entry_new(uint32_t arch, const char* url) { 25 | size_t url_len = strlen(url); 26 | struct ipv6_pxe_entry* ipe = malloc(sizeof(struct ipv6_pxe_entry) + url_len + 1); 27 | if (!ipe) 28 | return NULL; 29 | 30 | memcpy(ipe->bootfile_url.payload, url, url_len + 1); 31 | ipe->bootfile_url.len = htons(url_len); 32 | ipe->bootfile_url.type = htons(DHCPV6_OPT_BOOTFILE_URL); 33 | 34 | if (arch == 0xFFFFFFFF) { 35 | ipv6_pxe_default = ipe; 36 | } 37 | else { 38 | ipe->arch = arch; 39 | list_add(&ipe->list, &ipv6_pxe_list); 40 | } 41 | 42 | return ipe; 43 | } 44 | 45 | const struct ipv6_pxe_entry* ipv6_pxe_of_arch(uint16_t arch) { 46 | struct ipv6_pxe_entry* entry; 47 | list_for_each_entry(entry, &ipv6_pxe_list, list) { 48 | if (arch == entry->arch) 49 | return entry; 50 | } 51 | 52 | return ipv6_pxe_default; 53 | } 54 | 55 | void ipv6_pxe_serve_boot_url(uint16_t arch, struct iovec* iov) { 56 | const struct ipv6_pxe_entry* entry = ipv6_pxe_of_arch(arch); 57 | 58 | if (entry == NULL) { 59 | // No IPv6 PxE bootfile defined 60 | iov->iov_base = NULL; 61 | iov->iov_len = 0; 62 | } 63 | else { 64 | iov->iov_base = (void*)&(entry->bootfile_url); 65 | iov->iov_len = 4 + ntohs(entry->bootfile_url.len); 66 | syslog(LOG_INFO, "Serve IPv6 PxE, arch = %d, url = %s", arch, entry->bootfile_url.payload); 67 | } 68 | } 69 | 70 | void ipv6_pxe_dump(void) { 71 | struct ipv6_pxe_entry* entry; 72 | int count = 0; 73 | 74 | if (ipv6_pxe_default) 75 | count++; 76 | 77 | list_for_each_entry(entry, &ipv6_pxe_list, list) { 78 | count++; 79 | } 80 | 81 | if (count) { 82 | syslog(LOG_INFO, "IPv6 PxE URLs:\n"); 83 | 84 | list_for_each_entry(entry, &ipv6_pxe_list, list) { 85 | syslog(LOG_INFO, " arch %04d = %s\n", entry->arch, entry->bootfile_url.payload); 86 | } 87 | 88 | if (ipv6_pxe_default) 89 | syslog(LOG_INFO, " Default = %s\n", ipv6_pxe_default->bootfile_url.payload); 90 | } 91 | } 92 | 93 | void ipv6_pxe_clear(void) { 94 | struct ipv6_pxe_entry* entry, * temp; 95 | list_for_each_entry_safe(entry, temp, &ipv6_pxe_list, list) { 96 | list_del(&entry->list); 97 | free(entry); 98 | } 99 | 100 | if (ipv6_pxe_default) { 101 | free(ipv6_pxe_default); 102 | ipv6_pxe_default = NULL; 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/dhcpv6-pxe.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | // The detail is hidden except for dhcpv6-pxe.c 7 | struct ipv6_pxe_entry; 8 | 9 | const struct ipv6_pxe_entry* ipv6_pxe_entry_new(uint32_t arch, const char* url); 10 | const struct ipv6_pxe_entry* ipv6_pxe_of_arch(uint16_t arch); 11 | void ipv6_pxe_serve_boot_url(uint16_t arch, struct iovec* iov); 12 | void ipv6_pxe_dump(void); 13 | void ipv6_pxe_clear(void); 14 | -------------------------------------------------------------------------------- /src/dhcpv6.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2012-2013 Steven Barth 3 | * Copyright (C) 2018 Hans Dedecker 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License v2 as published by 7 | * the Free Software Foundation. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include 25 | 26 | #include "odhcpd.h" 27 | #include "dhcpv6.h" 28 | #include "dhcpv6-pxe.h" 29 | #ifdef DHCPV4_SUPPORT 30 | #include "dhcpv4.h" 31 | #endif 32 | 33 | static void relay_client_request(struct sockaddr_in6 *source, 34 | const void *data, size_t len, struct interface *iface); 35 | static void relay_server_response(uint8_t *data, size_t len); 36 | 37 | static void handle_dhcpv6(void *addr, void *data, size_t len, 38 | struct interface *iface, void *dest); 39 | static void handle_client_request(void *addr, void *data, size_t len, 40 | struct interface *iface, void *dest_addr); 41 | 42 | 43 | /* Create socket and register events */ 44 | int dhcpv6_init(void) 45 | { 46 | return dhcpv6_ia_init(); 47 | } 48 | 49 | int dhcpv6_setup_interface(struct interface *iface, bool enable) 50 | { 51 | int ret = 0; 52 | 53 | enable = enable && (iface->dhcpv6 != MODE_DISABLED); 54 | 55 | if (iface->dhcpv6_event.uloop.fd >= 0) { 56 | uloop_fd_delete(&iface->dhcpv6_event.uloop); 57 | close(iface->dhcpv6_event.uloop.fd); 58 | iface->dhcpv6_event.uloop.fd = -1; 59 | } 60 | 61 | /* Configure multicast settings */ 62 | if (enable) { 63 | struct sockaddr_in6 bind_addr = {AF_INET6, htons(DHCPV6_SERVER_PORT), 64 | 0, IN6ADDR_ANY_INIT, 0}; 65 | struct ipv6_mreq mreq; 66 | int val = 1; 67 | 68 | iface->dhcpv6_event.uloop.fd = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, IPPROTO_UDP); 69 | if (iface->dhcpv6_event.uloop.fd < 0) { 70 | syslog(LOG_ERR, "socket(AF_INET6): %m"); 71 | ret = -1; 72 | goto out; 73 | } 74 | 75 | /* Basic IPv6 configuration */ 76 | if (setsockopt(iface->dhcpv6_event.uloop.fd, SOL_SOCKET, SO_BINDTODEVICE, 77 | iface->ifname, strlen(iface->ifname)) < 0) { 78 | syslog(LOG_ERR, "setsockopt(SO_BINDTODEVICE): %m"); 79 | ret = -1; 80 | goto out; 81 | } 82 | 83 | if (setsockopt(iface->dhcpv6_event.uloop.fd, IPPROTO_IPV6, IPV6_V6ONLY, 84 | &val, sizeof(val)) < 0) { 85 | syslog(LOG_ERR, "setsockopt(IPV6_V6ONLY): %m"); 86 | ret = -1; 87 | goto out; 88 | } 89 | 90 | if (setsockopt(iface->dhcpv6_event.uloop.fd, SOL_SOCKET, SO_REUSEADDR, 91 | &val, sizeof(val)) < 0) { 92 | syslog(LOG_ERR, "setsockopt(SO_REUSEADDR): %m"); 93 | ret = -1; 94 | goto out; 95 | } 96 | 97 | if (setsockopt(iface->dhcpv6_event.uloop.fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, 98 | &val, sizeof(val)) < 0) { 99 | syslog(LOG_ERR, "setsockopt(IPV6_RECVPKTINFO): %m"); 100 | ret = -1; 101 | goto out; 102 | } 103 | 104 | val = DHCPV6_HOP_COUNT_LIMIT; 105 | if (setsockopt(iface->dhcpv6_event.uloop.fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, 106 | &val, sizeof(val)) < 0) { 107 | syslog(LOG_ERR, "setsockopt(IPV6_MULTICAST_HOPS): %m"); 108 | ret = -1; 109 | goto out; 110 | } 111 | 112 | val = 0; 113 | if (setsockopt(iface->dhcpv6_event.uloop.fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, 114 | &val, sizeof(val)) < 0) { 115 | syslog(LOG_ERR, "setsockopt(IPV6_MULTICAST_LOOP): %m"); 116 | ret = -1; 117 | goto out; 118 | } 119 | 120 | if (bind(iface->dhcpv6_event.uloop.fd, (struct sockaddr*)&bind_addr, 121 | sizeof(bind_addr)) < 0) { 122 | syslog(LOG_ERR, "bind(): %m"); 123 | ret = -1; 124 | goto out; 125 | } 126 | 127 | memset(&mreq, 0, sizeof(mreq)); 128 | inet_pton(AF_INET6, ALL_DHCPV6_RELAYS, &mreq.ipv6mr_multiaddr); 129 | mreq.ipv6mr_interface = iface->ifindex; 130 | 131 | if (setsockopt(iface->dhcpv6_event.uloop.fd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, 132 | &mreq, sizeof(mreq)) < 0) { 133 | syslog(LOG_ERR, "setsockopt(IPV6_ADD_MEMBERSHIP): %m"); 134 | ret = -1; 135 | goto out; 136 | } 137 | 138 | if (iface->dhcpv6 == MODE_SERVER) { 139 | memset(&mreq, 0, sizeof(mreq)); 140 | inet_pton(AF_INET6, ALL_DHCPV6_SERVERS, &mreq.ipv6mr_multiaddr); 141 | mreq.ipv6mr_interface = iface->ifindex; 142 | 143 | if (setsockopt(iface->dhcpv6_event.uloop.fd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, 144 | &mreq, sizeof(mreq)) < 0) { 145 | syslog(LOG_ERR, "setsockopt(IPV6_ADD_MEMBERSHIP): %m"); 146 | ret = -1; 147 | goto out; 148 | } 149 | } 150 | 151 | iface->dhcpv6_event.handle_dgram = handle_dhcpv6; 152 | odhcpd_register(&iface->dhcpv6_event); 153 | } 154 | 155 | ret = dhcpv6_ia_setup_interface(iface, enable); 156 | 157 | out: 158 | if (ret < 0 && iface->dhcpv6_event.uloop.fd >= 0) { 159 | close(iface->dhcpv6_event.uloop.fd); 160 | iface->dhcpv6_event.uloop.fd = -1; 161 | } 162 | 163 | return ret; 164 | } 165 | 166 | enum { 167 | IOV_NESTED = 0, 168 | IOV_DEST, 169 | IOV_MAXRT, 170 | #define IOV_STAT IOV_MAXRT 171 | IOV_RAPID_COMMIT, 172 | IOV_DNS, 173 | IOV_DNS_ADDR, 174 | IOV_SEARCH, 175 | IOV_SEARCH_DOMAIN, 176 | IOV_PDBUF, 177 | #define IOV_REFRESH IOV_PDBUF 178 | IOV_CERID, 179 | IOV_DHCPV6_RAW, 180 | IOV_NTP, 181 | IOV_NTP_ADDR, 182 | IOV_SNTP, 183 | IOV_SNTP_ADDR, 184 | IOV_RELAY_MSG, 185 | IOV_DHCPV4O6_SERVER, 186 | IOV_DNR, 187 | IOV_BOOTFILE_URL, 188 | IOV_TOTAL 189 | }; 190 | 191 | static void handle_nested_message(uint8_t *data, size_t len, 192 | struct dhcpv6_client_header **c_hdr, uint8_t **opts, 193 | uint8_t **end, struct iovec iov[IOV_TOTAL]) 194 | { 195 | struct dhcpv6_relay_header *r_hdr = (struct dhcpv6_relay_header *)data; 196 | uint16_t otype, olen; 197 | uint8_t *odata; 198 | 199 | if (iov[IOV_NESTED].iov_base == NULL) { 200 | iov[IOV_NESTED].iov_base = data; 201 | iov[IOV_NESTED].iov_len = len; 202 | } 203 | 204 | if (len < sizeof(struct dhcpv6_client_header)) 205 | return; 206 | 207 | if (r_hdr->msg_type != DHCPV6_MSG_RELAY_FORW) { 208 | iov[IOV_NESTED].iov_len = data - (uint8_t *)iov[IOV_NESTED].iov_base; 209 | *c_hdr = (void *)data; 210 | *opts = (uint8_t *)&(*c_hdr)[1]; 211 | *end = data + len; 212 | return; 213 | } 214 | 215 | dhcpv6_for_each_option(r_hdr->options, data + len, otype, olen, odata) { 216 | if (otype == DHCPV6_OPT_RELAY_MSG) { 217 | iov[IOV_RELAY_MSG].iov_base = odata + olen; 218 | iov[IOV_RELAY_MSG].iov_len = (((uint8_t *)iov[IOV_NESTED].iov_base) + 219 | iov[IOV_NESTED].iov_len) - (odata + olen); 220 | handle_nested_message(odata, olen, c_hdr, opts, end, iov); 221 | return; 222 | } 223 | } 224 | } 225 | 226 | 227 | static void update_nested_message(uint8_t *data, size_t len, ssize_t pdiff) 228 | { 229 | struct dhcpv6_relay_header *hdr = (struct dhcpv6_relay_header*)data; 230 | if (hdr->msg_type != DHCPV6_MSG_RELAY_FORW) 231 | return; 232 | 233 | hdr->msg_type = DHCPV6_MSG_RELAY_REPL; 234 | 235 | uint16_t otype, olen; 236 | uint8_t *odata; 237 | dhcpv6_for_each_option(hdr->options, data + len, otype, olen, odata) { 238 | if (otype == DHCPV6_OPT_RELAY_MSG) { 239 | olen += pdiff; 240 | odata[-2] = (olen >> 8) & 0xff; 241 | odata[-1] = olen & 0xff; 242 | update_nested_message(odata, olen - pdiff, pdiff); 243 | return; 244 | } 245 | } 246 | } 247 | 248 | #ifdef DHCPV4_SUPPORT 249 | 250 | struct dhcpv4_msg_data { 251 | uint8_t *msg; 252 | size_t maxsize; 253 | ssize_t len; 254 | }; 255 | 256 | static int send_reply(_unused const void *buf, size_t len, 257 | _unused const struct sockaddr *dest, _unused socklen_t dest_len, 258 | _unused void *opaque) 259 | { 260 | struct dhcpv4_msg_data *reply = opaque; 261 | 262 | if (len > reply->maxsize) { 263 | syslog(LOG_ERR, "4o6: reply too large, %zu > %zu", len, reply->maxsize); 264 | reply->len = -1; 265 | } else { 266 | memcpy(reply->msg, buf, len); 267 | reply->len = len; 268 | } 269 | 270 | return reply->len; 271 | } 272 | 273 | static ssize_t dhcpv6_4o6_query(uint8_t *buf, size_t buflen, 274 | struct interface *iface, 275 | const struct sockaddr_in6 *addr, 276 | const void *data, const uint8_t *end) 277 | { 278 | const struct dhcpv6_client_header *hdr = data; 279 | uint16_t otype, olen, msgv4_len = 0; 280 | uint8_t *msgv4_data = NULL; 281 | uint8_t *start = (uint8_t *)&hdr[1], *odata; 282 | struct sockaddr_in addrv4; 283 | struct dhcpv4_msg_data reply = { .msg = buf, .maxsize = buflen, .len = -1 }; 284 | 285 | dhcpv6_for_each_option(start, end, otype, olen, odata) { 286 | if (otype == DHCPV6_OPT_DHCPV4_MSG) { 287 | msgv4_data = odata; 288 | msgv4_len = olen; 289 | } 290 | } 291 | 292 | if (!msgv4_data || msgv4_len == 0) { 293 | syslog(LOG_ERR, "4o6: missing DHCPv4 message option (%d)", DHCPV6_OPT_DHCPV4_MSG); 294 | return -1; 295 | } 296 | 297 | // Dummy IPv4 address 298 | memset(&addrv4, 0, sizeof(addrv4)); 299 | addrv4.sin_family = AF_INET; 300 | addrv4.sin_addr.s_addr = INADDR_ANY; 301 | addrv4.sin_port = htons(DHCPV4_CLIENT_PORT); 302 | 303 | dhcpv4_handle_msg(&addrv4, msgv4_data, msgv4_len, 304 | iface, NULL, send_reply, &reply); 305 | 306 | return reply.len; 307 | } 308 | #endif /* DHCPV4_SUPPORT */ 309 | 310 | /* Simple DHCPv6-server for information requests */ 311 | static void handle_client_request(void *addr, void *data, size_t len, 312 | struct interface *iface, void *dest_addr) 313 | { 314 | struct dhcpv6_client_header *hdr = data; 315 | uint8_t *opts = (uint8_t *)&hdr[1], *opts_end = (uint8_t *)data + len; 316 | bool o_rapid_commit = false; 317 | 318 | if (len < sizeof(*hdr)) 319 | return; 320 | 321 | syslog(LOG_DEBUG, "Got a DHCPv6-request on %s", iface->name); 322 | 323 | /* Construct reply message */ 324 | struct __attribute__((packed)) { 325 | uint8_t msg_type; 326 | uint8_t tr_id[3]; 327 | uint16_t serverid_type; 328 | uint16_t serverid_length; 329 | uint16_t duid_type; 330 | uint16_t hardware_type; 331 | uint8_t mac[6]; 332 | uint16_t clientid_type; 333 | uint16_t clientid_length; 334 | uint8_t clientid_buf[130]; 335 | } dest = { 336 | .msg_type = DHCPV6_MSG_REPLY, 337 | .serverid_type = htons(DHCPV6_OPT_SERVERID), 338 | .serverid_length = htons(10), 339 | .duid_type = htons(3), 340 | .hardware_type = htons(1), 341 | .clientid_type = htons(DHCPV6_OPT_CLIENTID), 342 | .clientid_buf = {0} 343 | }; 344 | odhcpd_get_mac(iface, dest.mac); 345 | 346 | struct __attribute__((packed)) { 347 | uint16_t type; 348 | uint16_t len; 349 | uint32_t value; 350 | } maxrt = {htons(DHCPV6_OPT_SOL_MAX_RT), htons(sizeof(maxrt) - 4), 351 | htonl(60)}; 352 | 353 | struct __attribute__((packed)) { 354 | uint16_t type; 355 | uint16_t len; 356 | } rapid_commit = {htons(DHCPV6_OPT_RAPID_COMMIT), 0}; 357 | 358 | struct __attribute__((packed)) { 359 | uint16_t type; 360 | uint16_t len; 361 | uint16_t value; 362 | } stat = {htons(DHCPV6_OPT_STATUS), htons(sizeof(stat) - 4), 363 | htons(DHCPV6_STATUS_USEMULTICAST)}; 364 | 365 | struct __attribute__((packed)) { 366 | uint16_t type; 367 | uint16_t len; 368 | uint32_t value; 369 | } refresh = {htons(DHCPV6_OPT_INFO_REFRESH), htons(sizeof(uint32_t)), 370 | htonl(600)}; 371 | 372 | struct in6_addr dns_addr, *dns_addr_ptr = iface->dns; 373 | size_t dns_cnt = iface->dns_cnt; 374 | 375 | if ((dns_cnt == 0) && 376 | !odhcpd_get_interface_dns_addr(iface, &dns_addr)) { 377 | dns_addr_ptr = &dns_addr; 378 | dns_cnt = 1; 379 | } 380 | 381 | struct { 382 | uint16_t type; 383 | uint16_t len; 384 | } dns = {htons(DHCPV6_OPT_DNS_SERVERS), htons(dns_cnt * sizeof(*dns_addr_ptr))}; 385 | 386 | /* SNTP */ 387 | struct in6_addr *sntp_addr_ptr = iface->dhcpv6_sntp; 388 | size_t sntp_cnt = 0; 389 | struct { 390 | uint16_t type; 391 | uint16_t len; 392 | } dhcpv6_sntp; 393 | 394 | /* NTP */ 395 | uint8_t *ntp_ptr = iface->dhcpv6_ntp; 396 | uint16_t ntp_len = iface->dhcpv6_ntp_len; 397 | size_t ntp_cnt = 0; 398 | struct { 399 | uint16_t type; 400 | uint16_t len; 401 | } ntp; 402 | 403 | /* DNR */ 404 | struct dhcpv6_dnr { 405 | uint16_t type; 406 | uint16_t len; 407 | uint16_t priority; 408 | uint16_t adn_len; 409 | uint8_t body[]; 410 | }; 411 | struct dhcpv6_dnr *dnrs = NULL; 412 | size_t dnrs_len = 0; 413 | 414 | uint16_t otype, olen; 415 | uint8_t *odata; 416 | uint16_t *reqopts = NULL; 417 | size_t reqopts_cnt = 0; 418 | 419 | /* FIXME: this should be merged with the second loop further down */ 420 | dhcpv6_for_each_option(opts, opts_end, otype, olen, odata) { 421 | /* Requested options, array of uint16_t, RFC 8415 §21.7 */ 422 | if (otype == DHCPV6_OPT_ORO) { 423 | reqopts_cnt = olen / sizeof(uint16_t); 424 | reqopts = (uint16_t *)odata; 425 | break; 426 | } 427 | } 428 | 429 | /* Requested options */ 430 | for (size_t i = 0; i < reqopts_cnt; i++) { 431 | uint16_t opt = ntohs(reqopts[i]); 432 | 433 | switch (opt) { 434 | case DHCPV6_OPT_SNTP_SERVERS: 435 | sntp_cnt = iface->dhcpv6_sntp_cnt; 436 | dhcpv6_sntp.type = htons(DHCPV6_OPT_SNTP_SERVERS); 437 | dhcpv6_sntp.len = htons(sntp_cnt * sizeof(*sntp_addr_ptr)); 438 | break; 439 | 440 | case DHCPV6_OPT_NTP_SERVERS: 441 | ntp_cnt = iface->dhcpv6_ntp_cnt; 442 | ntp.type = htons(DHCPV6_OPT_NTP_SERVERS); 443 | ntp.len = htons(ntp_len); 444 | break; 445 | 446 | case DHCPV6_OPT_DNR: 447 | for (size_t i = 0; i < iface->dnr_cnt; i++) { 448 | struct dnr_options *dnr = &iface->dnr[i]; 449 | 450 | if (dnr->addr6_cnt == 0 && dnr->addr4_cnt > 0) 451 | continue; 452 | 453 | dnrs_len += sizeof(struct dhcpv6_dnr); 454 | dnrs_len += dnr->adn_len; 455 | 456 | if (dnr->addr6_cnt > 0 || dnr->svc_len > 0) { 457 | dnrs_len += sizeof(uint16_t); 458 | dnrs_len += dnr->addr6_cnt * sizeof(*dnr->addr6); 459 | dnrs_len += dnr->svc_len; 460 | } 461 | } 462 | 463 | dnrs = alloca(dnrs_len); 464 | uint8_t *pos = (uint8_t *)dnrs; 465 | 466 | for (size_t i = 0; i < iface->dnr_cnt; i++) { 467 | struct dnr_options *dnr = &iface->dnr[i]; 468 | struct dhcpv6_dnr *d6dnr = (struct dhcpv6_dnr *)pos; 469 | uint16_t d6dnr_type_be = htons(DHCPV6_OPT_DNR); 470 | uint16_t d6dnr_len = 2 * sizeof(uint16_t) + dnr->adn_len; 471 | uint16_t d6dnr_len_be; 472 | uint16_t d6dnr_priority_be = htons(dnr->priority); 473 | uint16_t d6dnr_adn_len_be = htons(dnr->adn_len); 474 | 475 | if (dnr->addr6_cnt == 0 && dnr->addr4_cnt > 0) 476 | continue; 477 | 478 | /* memcpy as the struct is unaligned */ 479 | memcpy(&d6dnr->type, &d6dnr_type_be, sizeof(d6dnr_type_be)); 480 | memcpy(&d6dnr->priority, &d6dnr_priority_be, sizeof(d6dnr_priority_be)); 481 | memcpy(&d6dnr->adn_len, &d6dnr_adn_len_be, sizeof(d6dnr_adn_len_be)); 482 | 483 | pos = d6dnr->body; 484 | memcpy(pos, dnr->adn, dnr->adn_len); 485 | pos += dnr->adn_len; 486 | 487 | if (dnr->addr6_cnt > 0 || dnr->svc_len > 0) { 488 | uint16_t addr6_len = dnr->addr6_cnt * sizeof(*dnr->addr6); 489 | uint16_t addr6_len_be = htons(addr6_len); 490 | 491 | memcpy(pos, &addr6_len_be, sizeof(addr6_len_be)); 492 | pos += sizeof(addr6_len_be); 493 | memcpy(pos, dnr->addr6, addr6_len); 494 | pos += addr6_len; 495 | memcpy(pos, dnr->svc, dnr->svc_len); 496 | pos += dnr->svc_len; 497 | 498 | d6dnr_len += sizeof(addr6_len_be) + addr6_len + dnr->svc_len; 499 | } 500 | 501 | d6dnr_len_be = htons(d6dnr_len); 502 | memcpy(&d6dnr->len, &d6dnr_len_be, sizeof(d6dnr_len_be)); 503 | } 504 | break; 505 | } 506 | } 507 | 508 | /* DNS Search options */ 509 | uint8_t search_buf[256], *search_domain = iface->search; 510 | size_t search_len = iface->search_len; 511 | 512 | if (!search_domain && !res_init() && _res.dnsrch[0] && _res.dnsrch[0][0]) { 513 | int len = dn_comp(_res.dnsrch[0], search_buf, 514 | sizeof(search_buf), NULL, NULL); 515 | if (len > 0) { 516 | search_domain = search_buf; 517 | search_len = len; 518 | } 519 | } 520 | 521 | struct { 522 | uint16_t type; 523 | uint16_t len; 524 | } search = {htons(DHCPV6_OPT_DNS_DOMAIN), htons(search_len)}; 525 | 526 | 527 | struct __attribute__((packed)) dhcpv4o6_server { 528 | uint16_t type; 529 | uint16_t len; 530 | struct in6_addr addr; 531 | } dhcpv4o6_server = {htons(DHCPV6_OPT_4O6_SERVER), htons(sizeof(struct in6_addr)), 532 | IN6ADDR_ANY_INIT}; 533 | 534 | struct dhcpv6_cer_id cerid = { 535 | #ifdef EXT_CER_ID 536 | .type = htons(EXT_CER_ID), 537 | #endif 538 | .len = htons(36), 539 | .addr = iface->dhcpv6_pd_cer, 540 | }; 541 | 542 | 543 | uint8_t pdbuf[512]; 544 | struct iovec iov[IOV_TOTAL] = { 545 | [IOV_NESTED] = {NULL, 0}, 546 | [IOV_DEST] = {&dest, (uint8_t*)&dest.clientid_type - (uint8_t*)&dest}, 547 | [IOV_MAXRT] = {&maxrt, sizeof(maxrt)}, 548 | [IOV_RAPID_COMMIT] = {&rapid_commit, 0}, 549 | [IOV_DNS] = {&dns, (dns_cnt) ? sizeof(dns) : 0}, 550 | [IOV_DNS_ADDR] = {dns_addr_ptr, dns_cnt * sizeof(*dns_addr_ptr)}, 551 | [IOV_SEARCH] = {&search, (search_len) ? sizeof(search) : 0}, 552 | [IOV_SEARCH_DOMAIN] = {search_domain, search_len}, 553 | [IOV_PDBUF] = {pdbuf, 0}, 554 | [IOV_CERID] = {&cerid, 0}, 555 | [IOV_DHCPV6_RAW] = {iface->dhcpv6_raw, iface->dhcpv6_raw_len}, 556 | [IOV_NTP] = {&ntp, (ntp_cnt) ? sizeof(ntp) : 0}, 557 | [IOV_NTP_ADDR] = {ntp_ptr, (ntp_cnt) ? ntp_len : 0}, 558 | [IOV_SNTP] = {&dhcpv6_sntp, (sntp_cnt) ? sizeof(dhcpv6_sntp) : 0}, 559 | [IOV_SNTP_ADDR] = {sntp_addr_ptr, sntp_cnt * sizeof(*sntp_addr_ptr)}, 560 | [IOV_DNR] = {dnrs, dnrs_len}, 561 | [IOV_RELAY_MSG] = {NULL, 0}, 562 | [IOV_DHCPV4O6_SERVER] = {&dhcpv4o6_server, 0}, 563 | [IOV_BOOTFILE_URL] = {NULL, 0} 564 | }; 565 | 566 | if (hdr->msg_type == DHCPV6_MSG_RELAY_FORW) 567 | handle_nested_message(data, len, &hdr, &opts, &opts_end, iov); 568 | 569 | switch (hdr->msg_type) { 570 | case DHCPV6_MSG_SOLICIT: 571 | case DHCPV6_MSG_REQUEST: 572 | case DHCPV6_MSG_CONFIRM: 573 | case DHCPV6_MSG_RENEW: 574 | case DHCPV6_MSG_REBIND: 575 | case DHCPV6_MSG_RELEASE: 576 | case DHCPV6_MSG_DECLINE: 577 | case DHCPV6_MSG_INFORMATION_REQUEST: 578 | case DHCPV6_MSG_RELAY_FORW: 579 | #ifdef DHCPV4_SUPPORT 580 | case DHCPV6_MSG_DHCPV4_QUERY: 581 | #endif 582 | break; /* Valid message types for clients */ 583 | case DHCPV6_MSG_ADVERTISE: 584 | case DHCPV6_MSG_REPLY: 585 | case DHCPV6_MSG_RECONFIGURE: 586 | case DHCPV6_MSG_RELAY_REPL: 587 | case DHCPV6_MSG_DHCPV4_RESPONSE: 588 | #ifndef DHCPV4_SUPPORT 589 | case DHCPV6_MSG_DHCPV4_QUERY: 590 | #endif 591 | default: 592 | return; /* Invalid message types for clients */ 593 | } 594 | 595 | if (!IN6_IS_ADDR_MULTICAST((struct in6_addr *)dest_addr) && iov[IOV_NESTED].iov_len == 0 && 596 | (hdr->msg_type == DHCPV6_MSG_SOLICIT || hdr->msg_type == DHCPV6_MSG_CONFIRM || 597 | hdr->msg_type == DHCPV6_MSG_REBIND || hdr->msg_type == DHCPV6_MSG_INFORMATION_REQUEST)) 598 | return; 599 | 600 | memcpy(dest.tr_id, hdr->transaction_id, sizeof(dest.tr_id)); 601 | 602 | /* Go through options and find what we need */ 603 | dhcpv6_for_each_option(opts, opts_end, otype, olen, odata) { 604 | if (otype == DHCPV6_OPT_CLIENTID && olen <= 130) { 605 | dest.clientid_length = htons(olen); 606 | memcpy(dest.clientid_buf, odata, olen); 607 | iov[IOV_DEST].iov_len += 4 + olen; 608 | } else if (otype == DHCPV6_OPT_SERVERID) { 609 | if (olen != ntohs(dest.serverid_length) || 610 | memcmp(odata, &dest.duid_type, olen)) 611 | return; /* Not for us */ 612 | } else if (iface->filter_class && otype == DHCPV6_OPT_USER_CLASS) { 613 | uint8_t *c = odata, *cend = &odata[olen]; 614 | for (; &c[2] <= cend && &c[2 + (c[0] << 8) + c[1]] <= cend; c = &c[2 + (c[0] << 8) + c[1]]) { 615 | size_t elen = strlen(iface->filter_class); 616 | if (((((size_t)c[0]) << 8) | c[1]) == elen && !memcmp(&c[2], iface->filter_class, elen)) 617 | return; /* Ignore from homenet */ 618 | } 619 | } else if (otype == DHCPV6_OPT_IA_PD) { 620 | #ifdef EXT_CER_ID 621 | iov[IOV_CERID].iov_len = sizeof(cerid); 622 | 623 | if (IN6_IS_ADDR_UNSPECIFIED(&cerid.addr)) { 624 | struct odhcpd_ipaddr *addrs; 625 | ssize_t len = netlink_get_interface_addrs(0, true, &addrs); 626 | 627 | for (ssize_t i = 0; i < len; ++i) 628 | if (IN6_IS_ADDR_UNSPECIFIED(&cerid.addr) 629 | || memcmp(&addrs[i].addr, &cerid.addr, sizeof(cerid.addr)) < 0) 630 | cerid.addr = addrs[i].addr.in6; 631 | 632 | free(addrs); 633 | } 634 | #endif 635 | } else if (otype == DHCPV6_OPT_RAPID_COMMIT && hdr->msg_type == DHCPV6_MSG_SOLICIT) { 636 | iov[IOV_RAPID_COMMIT].iov_len = sizeof(rapid_commit); 637 | o_rapid_commit = true; 638 | } else if (otype == DHCPV6_OPT_ORO) { 639 | for (int i=0; i < olen/2; i++) { 640 | uint16_t option = ntohs(((uint16_t *)odata)[i]); 641 | 642 | switch (option) { 643 | #ifdef DHCPV4_SUPPORT 644 | case DHCPV6_OPT_4O6_SERVER: 645 | if (iface->dhcpv4) { 646 | /* According to RFC 7341, 7.2. DHCP 4o6 Server Address Option Format: 647 | * This option may also carry no IPv6 addresses, which instructs the 648 | * client to use the All_DHCP_Relay_Agents_and_Servers multicast address 649 | * as the destination address. 650 | * 651 | * The ISC dhclient logs a missing IPv6 address as an error but seems to 652 | * work anyway: 653 | * dhcp4-o-dhcp6-server: expecting at least 16 bytes; got 0 654 | * 655 | * Include the All_DHCP_Relay_Agents_and_Servers multicast address 656 | * to make it explicit which address to use. */ 657 | struct dhcpv4o6_server *server = iov[IOV_DHCPV4O6_SERVER].iov_base; 658 | 659 | inet_pton(AF_INET6, ALL_DHCPV6_RELAYS, &server->addr); 660 | 661 | iov[IOV_DHCPV4O6_SERVER].iov_len = sizeof(dhcpv4o6_server); 662 | } 663 | break; 664 | #endif /* DHCPV4_SUPPORT */ 665 | default: 666 | break; 667 | } 668 | } 669 | } else if (otype == DHCPV6_OPT_CLIENT_ARCH) { 670 | uint16_t arch_code = ntohs(((uint16_t*)odata)[0]); 671 | ipv6_pxe_serve_boot_url(arch_code, &iov[IOV_BOOTFILE_URL]); 672 | } 673 | } 674 | 675 | if (!IN6_IS_ADDR_MULTICAST((struct in6_addr *)dest_addr) && iov[IOV_NESTED].iov_len == 0 && 676 | (hdr->msg_type == DHCPV6_MSG_REQUEST || hdr->msg_type == DHCPV6_MSG_RENEW || 677 | hdr->msg_type == DHCPV6_MSG_RELEASE || hdr->msg_type == DHCPV6_MSG_DECLINE)) { 678 | iov[IOV_STAT].iov_base = &stat; 679 | iov[IOV_STAT].iov_len = sizeof(stat); 680 | 681 | for (ssize_t i = IOV_STAT + 1; i < IOV_TOTAL; ++i) 682 | iov[i].iov_len = 0; 683 | 684 | odhcpd_send(iface->dhcpv6_event.uloop.fd, addr, iov, ARRAY_SIZE(iov), iface); 685 | return; 686 | } 687 | 688 | if (hdr->msg_type == DHCPV6_MSG_SOLICIT && !o_rapid_commit) { 689 | dest.msg_type = DHCPV6_MSG_ADVERTISE; 690 | } else if (hdr->msg_type == DHCPV6_MSG_INFORMATION_REQUEST) { 691 | iov[IOV_REFRESH].iov_base = &refresh; 692 | iov[IOV_REFRESH].iov_len = sizeof(refresh); 693 | 694 | /* Return inf max rt option in reply to information request */ 695 | maxrt.type = htons(DHCPV6_OPT_INF_MAX_RT); 696 | } 697 | 698 | #ifdef DHCPV4_SUPPORT 699 | if (hdr->msg_type == DHCPV6_MSG_DHCPV4_QUERY) { 700 | struct _packed dhcpv4_msg_data { 701 | uint16_t type; 702 | uint16_t len; 703 | uint8_t msg[1]; 704 | } *msg_opt = (struct dhcpv4_msg_data*)pdbuf; 705 | ssize_t msglen; 706 | 707 | memset(pdbuf, 0, sizeof(pdbuf)); 708 | 709 | msglen = dhcpv6_4o6_query(msg_opt->msg, sizeof(pdbuf) - sizeof(*msg_opt) + 1, 710 | iface, addr, (const void *)hdr, opts_end); 711 | if (msglen <= 0) { 712 | syslog(LOG_ERR, "4o6: query failed"); 713 | return; 714 | } 715 | 716 | msg_opt->type = htons(DHCPV6_OPT_DHCPV4_MSG); 717 | msg_opt->len = htons(msglen); 718 | iov[IOV_PDBUF].iov_len = sizeof(*msg_opt) - 1 + msglen; 719 | dest.msg_type = DHCPV6_MSG_DHCPV4_RESPONSE; 720 | } else 721 | #endif /* DHCPV4_SUPPORT */ 722 | 723 | if (hdr->msg_type != DHCPV6_MSG_INFORMATION_REQUEST) { 724 | ssize_t ialen = dhcpv6_ia_handle_IAs(pdbuf, sizeof(pdbuf), iface, addr, (const void *)hdr, opts_end); 725 | 726 | iov[IOV_PDBUF].iov_len = ialen; 727 | if (ialen < 0 || 728 | (ialen == 0 && (hdr->msg_type == DHCPV6_MSG_REBIND || hdr->msg_type == DHCPV6_MSG_CONFIRM))) 729 | return; 730 | } 731 | 732 | if (iov[IOV_NESTED].iov_len > 0) /* Update length */ 733 | update_nested_message(data, len, iov[IOV_DEST].iov_len + iov[IOV_MAXRT].iov_len + 734 | iov[IOV_RAPID_COMMIT].iov_len + iov[IOV_DNS].iov_len + 735 | iov[IOV_DNS_ADDR].iov_len + iov[IOV_SEARCH].iov_len + 736 | iov[IOV_SEARCH_DOMAIN].iov_len + iov[IOV_PDBUF].iov_len + 737 | iov[IOV_DHCPV4O6_SERVER].iov_len + 738 | iov[IOV_CERID].iov_len + iov[IOV_DHCPV6_RAW].iov_len + 739 | iov[IOV_NTP].iov_len + iov[IOV_NTP_ADDR].iov_len + 740 | iov[IOV_SNTP].iov_len + iov[IOV_SNTP_ADDR].iov_len + 741 | iov[IOV_DNR].iov_len + iov[IOV_BOOTFILE_URL].iov_len - 742 | (4 + opts_end - opts)); 743 | 744 | syslog(LOG_DEBUG, "Sending a DHCPv6-%s on %s", iov[IOV_NESTED].iov_len ? "relay-reply" : "reply", iface->name); 745 | 746 | odhcpd_send(iface->dhcpv6_event.uloop.fd, addr, iov, ARRAY_SIZE(iov), iface); 747 | } 748 | 749 | 750 | /* Central DHCPv6-relay handler */ 751 | static void handle_dhcpv6(void *addr, void *data, size_t len, 752 | struct interface *iface, void *dest_addr) 753 | { 754 | if (iface->dhcpv6 == MODE_SERVER) { 755 | handle_client_request(addr, data, len, iface, dest_addr); 756 | } else if (iface->dhcpv6 == MODE_RELAY) { 757 | if (iface->master) 758 | relay_server_response(data, len); 759 | else 760 | relay_client_request(addr, data, len, iface); 761 | } 762 | } 763 | 764 | 765 | /* Relay server response (regular relay server handling) */ 766 | static void relay_server_response(uint8_t *data, size_t len) 767 | { 768 | /* Information we need to gather */ 769 | uint8_t *payload_data = NULL; 770 | size_t payload_len = 0; 771 | int32_t ifaceidx = 0; 772 | struct sockaddr_in6 target = {AF_INET6, htons(DHCPV6_CLIENT_PORT), 773 | 0, IN6ADDR_ANY_INIT, 0}; 774 | int otype, olen; 775 | uint8_t *odata, *end = data + len; 776 | /* Relay DHCPv6 reply from server to client */ 777 | struct dhcpv6_relay_header *h = (void*)data; 778 | 779 | syslog(LOG_DEBUG, "Got a DHCPv6-relay-reply"); 780 | 781 | if (len < sizeof(*h) || h->msg_type != DHCPV6_MSG_RELAY_REPL) 782 | return; 783 | 784 | memcpy(&target.sin6_addr, &h->peer_address, sizeof(struct in6_addr)); 785 | 786 | /* Go through options and find what we need */ 787 | dhcpv6_for_each_option(h->options, end, otype, olen, odata) { 788 | if (otype == DHCPV6_OPT_INTERFACE_ID 789 | && olen == sizeof(ifaceidx)) { 790 | memcpy(&ifaceidx, odata, sizeof(ifaceidx)); 791 | } else if (otype == DHCPV6_OPT_RELAY_MSG) { 792 | payload_data = odata; 793 | payload_len = olen; 794 | } 795 | } 796 | 797 | /* Invalid interface-id or basic payload */ 798 | struct interface *iface = odhcpd_get_interface_by_index(ifaceidx); 799 | if (!iface || iface->master || !payload_data || payload_len < 4) 800 | return; 801 | 802 | bool is_authenticated = false; 803 | struct in6_addr *dns_ptr = NULL; 804 | size_t dns_count = 0; 805 | 806 | /* If the payload is relay-reply we have to send to the server port */ 807 | if (payload_data[0] == DHCPV6_MSG_RELAY_REPL) { 808 | target.sin6_port = htons(DHCPV6_SERVER_PORT); 809 | } else { /* Go through the payload data */ 810 | struct dhcpv6_client_header *h = (void*)payload_data; 811 | end = payload_data + payload_len; 812 | 813 | dhcpv6_for_each_option(&h[1], end, otype, olen, odata) { 814 | if (otype == DHCPV6_OPT_DNS_SERVERS && olen >= 16) { 815 | dns_ptr = (struct in6_addr*)odata; 816 | dns_count = olen / 16; 817 | } else if (otype == DHCPV6_OPT_AUTH) { 818 | is_authenticated = true; 819 | } 820 | } 821 | } 822 | 823 | /* Rewrite DNS servers if requested */ 824 | if (iface->always_rewrite_dns && dns_ptr && dns_count > 0) { 825 | if (is_authenticated) 826 | return; /* Impossible to rewrite */ 827 | 828 | const struct in6_addr *rewrite = iface->dns; 829 | struct in6_addr addr; 830 | size_t rewrite_cnt = iface->dns_cnt; 831 | 832 | if (rewrite_cnt == 0) { 833 | if (odhcpd_get_interface_dns_addr(iface, &addr)) 834 | return; /* Unable to get interface address */ 835 | 836 | rewrite = &addr; 837 | rewrite_cnt = 1; 838 | } 839 | 840 | /* Copy over any other addresses */ 841 | for (size_t i = 0; i < dns_count; ++i) { 842 | size_t j = (i < rewrite_cnt) ? i : rewrite_cnt - 1; 843 | memcpy(&dns_ptr[i], &rewrite[j], sizeof(*rewrite)); 844 | } 845 | } 846 | 847 | struct iovec iov = {payload_data, payload_len}; 848 | 849 | syslog(LOG_DEBUG, "Sending a DHCPv6-reply on %s", iface->name); 850 | 851 | odhcpd_send(iface->dhcpv6_event.uloop.fd, &target, &iov, 1, iface); 852 | } 853 | 854 | static struct odhcpd_ipaddr *relay_link_address(struct interface *iface) 855 | { 856 | struct odhcpd_ipaddr *addr = NULL; 857 | time_t now = odhcpd_time(); 858 | 859 | for (size_t i = 0; i < iface->addr6_len; i++) { 860 | if (iface->addr6[i].valid_lt <= (uint32_t)now) 861 | continue; 862 | 863 | if (iface->addr6[i].preferred_lt > (uint32_t)now) { 864 | addr = &iface->addr6[i]; 865 | break; 866 | } 867 | 868 | if (!addr || (iface->addr6[i].valid_lt > addr->valid_lt)) 869 | addr = &iface->addr6[i]; 870 | } 871 | 872 | return addr; 873 | } 874 | 875 | /* Relay client request (regular DHCPv6-relay) */ 876 | static void relay_client_request(struct sockaddr_in6 *source, 877 | const void *data, size_t len, struct interface *iface) 878 | { 879 | const struct dhcpv6_relay_header *h = data; 880 | /* Construct our forwarding envelope */ 881 | struct dhcpv6_relay_forward_envelope hdr = { 882 | .msg_type = DHCPV6_MSG_RELAY_FORW, 883 | .hop_count = 0, 884 | .interface_id_type = htons(DHCPV6_OPT_INTERFACE_ID), 885 | .interface_id_len = htons(sizeof(uint32_t)), 886 | .relay_message_type = htons(DHCPV6_OPT_RELAY_MSG), 887 | .relay_message_len = htons(len), 888 | }; 889 | struct iovec iov[2] = {{&hdr, sizeof(hdr)}, {(void *)data, len}}; 890 | struct interface *c; 891 | struct odhcpd_ipaddr *ip; 892 | struct sockaddr_in6 s; 893 | 894 | if (h->msg_type == DHCPV6_MSG_RELAY_REPL || 895 | h->msg_type == DHCPV6_MSG_RECONFIGURE || 896 | h->msg_type == DHCPV6_MSG_REPLY || 897 | h->msg_type == DHCPV6_MSG_ADVERTISE) 898 | return; /* Invalid message types for client */ 899 | 900 | syslog(LOG_DEBUG, "Got a DHCPv6-request on %s", iface->name); 901 | 902 | if (h->msg_type == DHCPV6_MSG_RELAY_FORW) { /* handle relay-forward */ 903 | if (h->hop_count >= DHCPV6_HOP_COUNT_LIMIT) 904 | return; /* Invalid hop count */ 905 | 906 | hdr.hop_count = h->hop_count + 1; 907 | } 908 | 909 | /* use memcpy here as the destination fields are unaligned */ 910 | memcpy(&hdr.peer_address, &source->sin6_addr, sizeof(struct in6_addr)); 911 | memcpy(&hdr.interface_id_data, &iface->ifindex, sizeof(iface->ifindex)); 912 | 913 | /* Detect public IP of slave interface to use as link-address */ 914 | ip = relay_link_address(iface); 915 | if (ip) 916 | memcpy(&hdr.link_address, &ip->addr.in6, sizeof(hdr.link_address)); 917 | 918 | memset(&s, 0, sizeof(s)); 919 | s.sin6_family = AF_INET6; 920 | s.sin6_port = htons(DHCPV6_SERVER_PORT); 921 | inet_pton(AF_INET6, ALL_DHCPV6_SERVERS, &s.sin6_addr); 922 | 923 | avl_for_each_element(&interfaces, c, avl) { 924 | if (!c->master || c->dhcpv6 != MODE_RELAY) 925 | continue; 926 | 927 | if (!ip) { 928 | /* No suitable address! Is the slave not configured yet? 929 | * Detect public IP of master interface and use it instead 930 | * This is WRONG and probably violates the RFC. However 931 | * otherwise we have a hen and egg problem because the 932 | * slave-interface cannot be auto-configured. */ 933 | ip = relay_link_address(c); 934 | if (!ip) 935 | continue; /* Could not obtain a suitable address */ 936 | 937 | memcpy(&hdr.link_address, &ip->addr.in6, sizeof(hdr.link_address)); 938 | ip = NULL; 939 | } 940 | 941 | syslog(LOG_DEBUG, "Sending a DHCPv6-relay-forward on %s", c->name); 942 | 943 | odhcpd_send(c->dhcpv6_event.uloop.fd, &s, iov, 2, c); 944 | } 945 | } 946 | -------------------------------------------------------------------------------- /src/dhcpv6.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2012 Steven Barth 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License version 2 6 | * as published by the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License version 2 for more details. 12 | * 13 | */ 14 | #pragma once 15 | 16 | #include "odhcpd.h" 17 | 18 | #define ALL_DHCPV6_RELAYS "ff02::1:2" 19 | 20 | #define ALL_DHCPV6_SERVERS "ff05::1:3" 21 | 22 | #define DHCPV6_CLIENT_PORT 546 23 | #define DHCPV6_SERVER_PORT 547 24 | 25 | #define DHCPV6_MSG_SOLICIT 1 26 | #define DHCPV6_MSG_ADVERTISE 2 27 | #define DHCPV6_MSG_REQUEST 3 28 | #define DHCPV6_MSG_CONFIRM 4 29 | #define DHCPV6_MSG_RENEW 5 30 | #define DHCPV6_MSG_REBIND 6 31 | #define DHCPV6_MSG_REPLY 7 32 | #define DHCPV6_MSG_RELEASE 8 33 | #define DHCPV6_MSG_DECLINE 9 34 | #define DHCPV6_MSG_RECONFIGURE 10 35 | #define DHCPV6_MSG_INFORMATION_REQUEST 11 36 | #define DHCPV6_MSG_RELAY_FORW 12 37 | #define DHCPV6_MSG_RELAY_REPL 13 38 | #define DHCPV6_MSG_DHCPV4_QUERY 20 39 | #define DHCPV6_MSG_DHCPV4_RESPONSE 21 40 | 41 | #define DHCPV6_OPT_CLIENTID 1 42 | #define DHCPV6_OPT_SERVERID 2 43 | #define DHCPV6_OPT_IA_NA 3 44 | #define DHCPV6_OPT_IA_ADDR 5 45 | #define DHCPV6_OPT_ORO 6 46 | #define DHCPV6_OPT_STATUS 13 47 | #define DHCPV6_OPT_RELAY_MSG 9 48 | #define DHCPV6_OPT_AUTH 11 49 | #define DHCPV6_OPT_RAPID_COMMIT 14 50 | #define DHCPV6_OPT_USER_CLASS 15 51 | #define DHCPV6_OPT_INTERFACE_ID 18 52 | #define DHCPV6_OPT_RECONF_MSG 19 53 | #define DHCPV6_OPT_RECONF_ACCEPT 20 54 | #define DHCPV6_OPT_DNS_SERVERS 23 55 | #define DHCPV6_OPT_DNS_DOMAIN 24 56 | #define DHCPV6_OPT_IA_PD 25 57 | #define DHCPV6_OPT_IA_PREFIX 26 58 | #define DHCPV6_OPT_SNTP_SERVERS 31 59 | #define DHCPV6_OPT_INFO_REFRESH 32 60 | #define DHCPV6_OPT_FQDN 39 61 | #define DHCPV6_OPT_NTP_SERVERS 56 62 | #define DHCPV6_OPT_BOOTFILE_URL 59 63 | #define DHCPV6_OPT_BOOTFILE_PARAM 60 64 | #define DHCPV6_OPT_CLIENT_ARCH 61 65 | #define DHCPV6_OPT_SOL_MAX_RT 82 66 | #define DHCPV6_OPT_INF_MAX_RT 83 67 | #define DHCPV6_OPT_DHCPV4_MSG 87 68 | #define DHCPV6_OPT_4O6_SERVER 88 69 | #define DHCPV6_OPT_DNR 144 70 | 71 | #define DHCPV6_DUID_VENDOR 2 72 | 73 | #define DHCPV6_STATUS_OK 0 74 | #define DHCPV6_STATUS_NOADDRSAVAIL 2 75 | #define DHCPV6_STATUS_NOBINDING 3 76 | #define DHCPV6_STATUS_NOTONLINK 4 77 | #define DHCPV6_STATUS_USEMULTICAST 5 78 | #define DHCPV6_STATUS_NOPREFIXAVAIL 6 79 | 80 | // I just remembered I have an old one lying around... 81 | #define DHCPV6_ENT_NO 30462 82 | #define DHCPV6_ENT_TYPE 1 83 | 84 | 85 | #define DHCPV6_HOP_COUNT_LIMIT 32 86 | 87 | #define DHCPV6_REC_TIMEOUT 2000 /* msec */ 88 | #define DHCPV6_REC_MAX_RC 8 89 | 90 | struct dhcpv6_client_header { 91 | uint8_t msg_type; 92 | uint8_t transaction_id[3]; 93 | } __attribute__((packed)); 94 | 95 | struct dhcpv6_relay_header { 96 | uint8_t msg_type; 97 | uint8_t hop_count; 98 | struct in6_addr link_address; 99 | struct in6_addr peer_address; 100 | uint8_t options[]; 101 | } __attribute__((packed)); 102 | 103 | struct dhcpv6_relay_forward_envelope { 104 | uint8_t msg_type; 105 | uint8_t hop_count; 106 | struct in6_addr link_address; 107 | struct in6_addr peer_address; 108 | uint16_t interface_id_type; 109 | uint16_t interface_id_len; 110 | uint32_t interface_id_data; 111 | uint16_t relay_message_type; 112 | uint16_t relay_message_len; 113 | } __attribute__((packed)); 114 | 115 | struct dhcpv6_auth_reconfigure { 116 | uint16_t type; 117 | uint16_t len; 118 | uint8_t protocol; 119 | uint8_t algorithm; 120 | uint8_t rdm; 121 | uint32_t replay[2]; 122 | uint8_t reconf_type; 123 | uint8_t key[16]; 124 | } _packed; 125 | 126 | struct dhcpv6_ia_hdr { 127 | uint16_t type; 128 | uint16_t len; 129 | uint32_t iaid; 130 | uint32_t t1; 131 | uint32_t t2; 132 | } _packed; 133 | 134 | struct dhcpv6_ia_prefix { 135 | uint16_t type; 136 | uint16_t len; 137 | uint32_t preferred_lt; 138 | uint32_t valid_lt; 139 | uint8_t prefix; 140 | struct in6_addr addr; 141 | } _packed; 142 | 143 | struct dhcpv6_ia_addr { 144 | uint16_t type; 145 | uint16_t len; 146 | struct in6_addr addr; 147 | uint32_t preferred_lt; 148 | uint32_t valid_lt; 149 | } _packed; 150 | 151 | struct dhcpv6_cer_id { 152 | uint16_t type; 153 | uint16_t len; 154 | uint16_t reserved; 155 | uint16_t auth_type; 156 | uint8_t auth[16]; 157 | struct in6_addr addr; 158 | }; 159 | 160 | #define dhcpv6_for_each_option(start, end, otype, olen, odata)\ 161 | for (uint8_t *_o = (uint8_t*)(start); _o + 4 <= (end) &&\ 162 | ((otype) = _o[0] << 8 | _o[1]) && ((odata) = (void*)&_o[4]) &&\ 163 | ((olen) = _o[2] << 8 | _o[3]) + (odata) <= (end); \ 164 | _o += 4 + (_o[2] << 8 | _o[3])) 165 | -------------------------------------------------------------------------------- /src/ndp.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2012-2013 Steven Barth 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License v2 as published by 6 | * the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | * 13 | */ 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include 30 | #include 31 | 32 | #include "dhcpv6.h" 33 | #include "odhcpd.h" 34 | 35 | 36 | static void ndp_netevent_cb(unsigned long event, struct netevent_handler_info *info); 37 | static void setup_route(struct in6_addr *addr, struct interface *iface, bool add); 38 | static void setup_addr_for_relaying(struct in6_addr *addr, struct interface *iface, bool add); 39 | static void handle_solicit(void *addr, void *data, size_t len, 40 | struct interface *iface, void *dest); 41 | 42 | /* Filter ICMPv6 messages of type neighbor solicitation */ 43 | static struct sock_filter bpf[] = { 44 | BPF_STMT(BPF_LD | BPF_B | BPF_ABS, offsetof(struct ip6_hdr, ip6_nxt)), 45 | BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, IPPROTO_ICMPV6, 0, 3), 46 | BPF_STMT(BPF_LD | BPF_B | BPF_ABS, sizeof(struct ip6_hdr) + 47 | offsetof(struct icmp6_hdr, icmp6_type)), 48 | BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ND_NEIGHBOR_SOLICIT, 0, 1), 49 | BPF_STMT(BPF_RET | BPF_K, 0xffffffff), 50 | BPF_STMT(BPF_RET | BPF_K, 0), 51 | }; 52 | static const struct sock_fprog bpf_prog = {sizeof(bpf) / sizeof(*bpf), bpf}; 53 | static struct netevent_handler ndp_netevent_handler = { .cb = ndp_netevent_cb, }; 54 | 55 | /* Initialize NDP-proxy */ 56 | int ndp_init(void) 57 | { 58 | int ret = 0; 59 | 60 | if (netlink_add_netevent_handler(&ndp_netevent_handler) < 0) { 61 | syslog(LOG_ERR, "Failed to add ndp netevent handler"); 62 | ret = -1; 63 | } 64 | 65 | return ret; 66 | } 67 | 68 | int ndp_setup_interface(struct interface *iface, bool enable) 69 | { 70 | int ret = 0, procfd; 71 | bool dump_neigh = false; 72 | char procbuf[64]; 73 | 74 | enable = enable && (iface->ndp == MODE_RELAY); 75 | 76 | snprintf(procbuf, sizeof(procbuf), "/proc/sys/net/ipv6/conf/%s/proxy_ndp", iface->ifname); 77 | procfd = open(procbuf, O_WRONLY); 78 | 79 | if (procfd < 0) { 80 | ret = -1; 81 | goto out; 82 | } 83 | 84 | if (iface->ndp_ping_fd >= 0) { 85 | close(iface->ndp_ping_fd); 86 | iface->ndp_ping_fd = -1; 87 | } 88 | 89 | if (iface->ndp_event.uloop.fd >= 0) { 90 | uloop_fd_delete(&iface->ndp_event.uloop); 91 | close(iface->ndp_event.uloop.fd); 92 | iface->ndp_event.uloop.fd = -1; 93 | 94 | if (!enable) 95 | if (write(procfd, "0\n", 2) < 0) {} 96 | 97 | dump_neigh = true; 98 | } 99 | 100 | if (enable) { 101 | struct sockaddr_ll ll; 102 | struct packet_mreq mreq; 103 | struct icmp6_filter filt; 104 | int val = 2; 105 | 106 | if (write(procfd, "1\n", 2) < 0) {} 107 | 108 | /* Open ICMPv6 socket */ 109 | iface->ndp_ping_fd = socket(AF_INET6, SOCK_RAW | SOCK_CLOEXEC, IPPROTO_ICMPV6); 110 | if (iface->ndp_ping_fd < 0) { 111 | syslog(LOG_ERR, "socket(AF_INET6): %m"); 112 | ret = -1; 113 | goto out; 114 | } 115 | 116 | if (setsockopt(iface->ndp_ping_fd, SOL_SOCKET, SO_BINDTODEVICE, 117 | iface->ifname, strlen(iface->ifname)) < 0) { 118 | syslog(LOG_ERR, "setsockopt(SO_BINDTODEVICE): %m"); 119 | ret = -1; 120 | goto out; 121 | } 122 | 123 | if (setsockopt(iface->ndp_ping_fd, IPPROTO_RAW, IPV6_CHECKSUM, 124 | &val, sizeof(val)) < 0) { 125 | syslog(LOG_ERR, "setsockopt(IPV6_CHECKSUM): %m"); 126 | ret = -1; 127 | goto out; 128 | } 129 | 130 | /* This is required by RFC 4861 */ 131 | val = 255; 132 | if (setsockopt(iface->ndp_ping_fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, 133 | &val, sizeof(val)) < 0) { 134 | syslog(LOG_ERR, "setsockopt(IPV6_MULTICAST_HOPS): %m"); 135 | ret = -1; 136 | goto out; 137 | } 138 | 139 | if (setsockopt(iface->ndp_ping_fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, 140 | &val, sizeof(val)) < 0) { 141 | syslog(LOG_ERR, "setsockopt(IPV6_UNICAST_HOPS): %m"); 142 | ret = -1; 143 | goto out; 144 | } 145 | 146 | /* Filter all packages, we only want to send */ 147 | ICMP6_FILTER_SETBLOCKALL(&filt); 148 | if (setsockopt(iface->ndp_ping_fd, IPPROTO_ICMPV6, ICMP6_FILTER, 149 | &filt, sizeof(filt)) < 0) { 150 | syslog(LOG_ERR, "setsockopt(ICMP6_FILTER): %m"); 151 | ret = -1; 152 | goto out; 153 | } 154 | 155 | 156 | iface->ndp_event.uloop.fd = socket(AF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC, htons(ETH_P_IPV6)); 157 | if (iface->ndp_event.uloop.fd < 0) { 158 | syslog(LOG_ERR, "socket(AF_PACKET): %m"); 159 | ret = -1; 160 | goto out; 161 | } 162 | 163 | #ifdef PACKET_RECV_TYPE 164 | int pktt = 1 << PACKET_MULTICAST; 165 | if (setsockopt(iface->ndp_event.uloop.fd, SOL_PACKET, PACKET_RECV_TYPE, 166 | &pktt, sizeof(pktt)) < 0) { 167 | syslog(LOG_ERR, "setsockopt(PACKET_RECV_TYPE): %m"); 168 | ret = -1; 169 | goto out; 170 | } 171 | #endif 172 | 173 | if (setsockopt(iface->ndp_event.uloop.fd, SOL_SOCKET, SO_ATTACH_FILTER, 174 | &bpf_prog, sizeof(bpf_prog))) { 175 | syslog(LOG_ERR, "setsockopt(SO_ATTACH_FILTER): %m"); 176 | ret = -1; 177 | goto out; 178 | } 179 | 180 | memset(&ll, 0, sizeof(ll)); 181 | ll.sll_family = AF_PACKET; 182 | ll.sll_ifindex = iface->ifindex; 183 | ll.sll_protocol = htons(ETH_P_IPV6); 184 | 185 | if (bind(iface->ndp_event.uloop.fd, (struct sockaddr*)&ll, sizeof(ll)) < 0) { 186 | syslog(LOG_ERR, "bind(): %m"); 187 | ret = -1; 188 | goto out; 189 | } 190 | 191 | memset(&mreq, 0, sizeof(mreq)); 192 | mreq.mr_ifindex = iface->ifindex; 193 | mreq.mr_type = PACKET_MR_ALLMULTI; 194 | mreq.mr_alen = ETH_ALEN; 195 | 196 | if (setsockopt(iface->ndp_event.uloop.fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, 197 | &mreq, sizeof(mreq)) < 0) { 198 | syslog(LOG_ERR, "setsockopt(PACKET_ADD_MEMBERSHIP): %m"); 199 | ret = -1; 200 | goto out; 201 | } 202 | 203 | iface->ndp_event.handle_dgram = handle_solicit; 204 | odhcpd_register(&iface->ndp_event); 205 | 206 | /* If we already were enabled dump is unnecessary, if not do dump */ 207 | if (!dump_neigh) 208 | netlink_dump_neigh_table(false); 209 | else 210 | dump_neigh = false; 211 | } 212 | 213 | if (dump_neigh) 214 | netlink_dump_neigh_table(true); 215 | 216 | out: 217 | if (ret < 0) { 218 | if (iface->ndp_event.uloop.fd >= 0) { 219 | close(iface->ndp_event.uloop.fd); 220 | iface->ndp_event.uloop.fd = -1; 221 | } 222 | 223 | if (iface->ndp_ping_fd >= 0) { 224 | close(iface->ndp_ping_fd); 225 | iface->ndp_ping_fd = -1; 226 | } 227 | } 228 | 229 | if (procfd >= 0) 230 | close(procfd); 231 | 232 | return ret; 233 | } 234 | 235 | static void ndp_netevent_cb(unsigned long event, struct netevent_handler_info *info) 236 | { 237 | struct interface *iface = info->iface; 238 | bool add = true; 239 | 240 | if (!iface || iface->ndp == MODE_DISABLED) 241 | return; 242 | 243 | switch (event) { 244 | case NETEV_ADDR6_DEL: 245 | add = false; 246 | netlink_dump_neigh_table(false); 247 | /* fall through */ 248 | case NETEV_ADDR6_ADD: 249 | setup_addr_for_relaying(&info->addr.in6, iface, add); 250 | break; 251 | case NETEV_NEIGH6_DEL: 252 | add = false; 253 | /* fall through */ 254 | case NETEV_NEIGH6_ADD: 255 | if (info->neigh.flags & NTF_PROXY) { 256 | if (add) { 257 | netlink_setup_proxy_neigh(&info->neigh.dst.in6, iface->ifindex, false); 258 | setup_route(&info->neigh.dst.in6, iface, false); 259 | netlink_dump_neigh_table(false); 260 | } 261 | break; 262 | } 263 | 264 | if (add && 265 | !(info->neigh.state & 266 | (NUD_REACHABLE|NUD_STALE|NUD_DELAY|NUD_PROBE|NUD_PERMANENT|NUD_NOARP))) 267 | break; 268 | 269 | setup_addr_for_relaying(&info->neigh.dst.in6, iface, add); 270 | setup_route(&info->neigh.dst.in6, iface, add); 271 | 272 | if (!add) 273 | netlink_dump_neigh_table(false); 274 | break; 275 | default: 276 | break; 277 | } 278 | } 279 | 280 | /* Send an ICMP-ECHO. This is less for actually pinging but for the 281 | * neighbor cache to be kept up-to-date. */ 282 | static void ping6(struct in6_addr *addr, 283 | const struct interface *iface) 284 | { 285 | struct sockaddr_in6 dest = { .sin6_family = AF_INET6, .sin6_addr = *addr , }; 286 | struct icmp6_hdr echo = { .icmp6_type = ICMP6_ECHO_REQUEST }; 287 | struct iovec iov = { .iov_base = &echo, .iov_len = sizeof(echo) }; 288 | char ipbuf[INET6_ADDRSTRLEN]; 289 | 290 | inet_ntop(AF_INET6, addr, ipbuf, sizeof(ipbuf)); 291 | syslog(LOG_DEBUG, "Pinging for %s on %s", ipbuf, iface->name); 292 | 293 | netlink_setup_route(addr, 128, iface->ifindex, NULL, 128, true); 294 | odhcpd_send(iface->ndp_ping_fd, &dest, &iov, 1, iface); 295 | netlink_setup_route(addr, 128, iface->ifindex, NULL, 128, false); 296 | } 297 | 298 | /* Send a Neighbor Advertisement. */ 299 | static void send_na(struct in6_addr *to_addr, 300 | const struct interface *iface, struct in6_addr *for_addr, 301 | const uint8_t *mac) 302 | { 303 | struct sockaddr_in6 dest = { .sin6_family = AF_INET6, .sin6_addr = *to_addr }; 304 | char pbuf[sizeof(struct nd_neighbor_advert) + sizeof(struct nd_opt_hdr) + 6]; 305 | struct nd_neighbor_advert *adv = (struct nd_neighbor_advert*)pbuf; 306 | struct nd_opt_hdr *opt = (struct nd_opt_hdr*) &pbuf[sizeof(struct nd_neighbor_advert)]; 307 | struct iovec iov = { .iov_base = &pbuf, .iov_len = sizeof(pbuf) }; 308 | char ipbuf[INET6_ADDRSTRLEN]; 309 | 310 | memset(pbuf, 0, sizeof(pbuf)); 311 | adv->nd_na_hdr = (struct icmp6_hdr) { 312 | .icmp6_type = ND_NEIGHBOR_ADVERT, 313 | .icmp6_dataun.icmp6_un_data32 = { ND_NA_FLAG_SOLICITED } 314 | }; 315 | adv->nd_na_target = *for_addr; 316 | *opt = (struct nd_opt_hdr) { .nd_opt_type = ND_OPT_TARGET_LINKADDR, .nd_opt_len = 1 }; 317 | memcpy(&pbuf[sizeof(struct nd_neighbor_advert) + sizeof(struct nd_opt_hdr)], mac, 6); 318 | 319 | inet_ntop(AF_INET6, to_addr, ipbuf, sizeof(ipbuf)); 320 | syslog(LOG_DEBUG, "Answering NS to %s on %s", ipbuf, iface->ifname); 321 | 322 | odhcpd_send(iface->ndp_ping_fd, &dest, &iov, 1, iface); 323 | } 324 | 325 | /* Handle solicitations */ 326 | static void handle_solicit(void *addr, void *data, size_t len, 327 | struct interface *iface, _unused void *dest) 328 | { 329 | struct ip6_hdr *ip6 = data; 330 | struct nd_neighbor_solicit *req = (struct nd_neighbor_solicit*)&ip6[1]; 331 | struct sockaddr_ll *ll = addr; 332 | struct interface *c; 333 | char ipbuf[INET6_ADDRSTRLEN]; 334 | uint8_t mac[6]; 335 | 336 | /* Solicitation is for duplicate address detection */ 337 | bool ns_is_dad = IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_src); 338 | 339 | /* Don't process solicit messages on non relay interfaces 340 | * Don't forward any non-DAD solicitation for external ifaces 341 | * TODO: check if we should even forward DADs for them */ 342 | if (iface->ndp != MODE_RELAY || (iface->external && !ns_is_dad)) 343 | return; 344 | 345 | if (len < sizeof(*ip6) + sizeof(*req)) 346 | return; // Invalid total length 347 | 348 | if (IN6_IS_ADDR_LINKLOCAL(&req->nd_ns_target) || 349 | IN6_IS_ADDR_LOOPBACK(&req->nd_ns_target) || 350 | IN6_IS_ADDR_MULTICAST(&req->nd_ns_target)) 351 | return; /* Invalid target */ 352 | 353 | inet_ntop(AF_INET6, &req->nd_ns_target, ipbuf, sizeof(ipbuf)); 354 | syslog(LOG_DEBUG, "Got a NS for %s on %s", ipbuf, iface->name); 355 | 356 | odhcpd_get_mac(iface, mac); 357 | if (!memcmp(ll->sll_addr, mac, sizeof(mac))) 358 | return; /* Looped back */ 359 | 360 | avl_for_each_element(&interfaces, c, avl) { 361 | if (iface != c && c->ndp == MODE_RELAY && 362 | (ns_is_dad || !c->external)) 363 | ping6(&req->nd_ns_target, c); 364 | } 365 | 366 | /* Catch global-addressed NS and answer them manually. 367 | * The kernel won't answer these and cannot route them either. */ 368 | if (!IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) && 369 | IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_src)) { 370 | bool is_proxy_neigh = netlink_get_interface_proxy_neigh(iface->ifindex, 371 | &req->nd_ns_target) == 1; 372 | 373 | if (is_proxy_neigh) 374 | send_na(&ip6->ip6_src, iface, &req->nd_ns_target, mac); 375 | } 376 | } 377 | 378 | /* Use rtnetlink to modify kernel routes */ 379 | static void setup_route(struct in6_addr *addr, struct interface *iface, bool add) 380 | { 381 | char ipbuf[INET6_ADDRSTRLEN]; 382 | 383 | inet_ntop(AF_INET6, addr, ipbuf, sizeof(ipbuf)); 384 | syslog(LOG_DEBUG, "%s about %s%s on %s", 385 | (add) ? "Learning" : "Forgetting", 386 | iface->learn_routes ? "proxy routing for " : "", 387 | ipbuf, iface->name); 388 | 389 | if (iface->learn_routes) 390 | netlink_setup_route(addr, 128, iface->ifindex, NULL, 1024, add); 391 | } 392 | 393 | static void setup_addr_for_relaying(struct in6_addr *addr, struct interface *iface, bool add) 394 | { 395 | struct interface *c; 396 | char ipbuf[INET6_ADDRSTRLEN]; 397 | 398 | inet_ntop(AF_INET6, addr, ipbuf, sizeof(ipbuf)); 399 | 400 | avl_for_each_element(&interfaces, c, avl) { 401 | if (iface == c || c->ndp != MODE_RELAY) 402 | continue; 403 | 404 | if (netlink_setup_proxy_neigh(addr, c->ifindex, add)) { 405 | if (add) 406 | syslog(LOG_ERR, "Failed to add proxy neighbour entry %s on %s", 407 | ipbuf, c->name); 408 | } else 409 | syslog(LOG_DEBUG, "%s proxy neighbour entry %s on %s", 410 | add ? "Added" : "Deleted", ipbuf, c->name); 411 | } 412 | } 413 | -------------------------------------------------------------------------------- /src/netlink.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2017 Hans Dedecker 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License v2 as published by 6 | * the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | * 13 | */ 14 | 15 | #include 16 | #include 17 | #include 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include 25 | #include 26 | #include 27 | 28 | #include 29 | #include 30 | 31 | #include "odhcpd.h" 32 | 33 | struct event_socket { 34 | struct odhcpd_event ev; 35 | struct nl_sock *sock; 36 | int sock_bufsize; 37 | }; 38 | 39 | static void handle_rtnl_event(struct odhcpd_event *ev); 40 | static int cb_rtnl_valid(struct nl_msg *msg, void *arg); 41 | static void catch_rtnl_err(struct odhcpd_event *e, int error); 42 | static struct nl_sock *create_socket(int protocol); 43 | 44 | static struct nl_sock *rtnl_socket = NULL; 45 | struct list_head netevent_handler_list = LIST_HEAD_INIT(netevent_handler_list); 46 | static struct event_socket rtnl_event = { 47 | .ev = { 48 | .uloop = {.fd = - 1, }, 49 | .handle_dgram = NULL, 50 | .handle_error = catch_rtnl_err, 51 | .recv_msgs = handle_rtnl_event, 52 | }, 53 | .sock = NULL, 54 | .sock_bufsize = 133120, 55 | }; 56 | 57 | int netlink_init(void) 58 | { 59 | rtnl_socket = create_socket(NETLINK_ROUTE); 60 | if (!rtnl_socket) { 61 | syslog(LOG_ERR, "Unable to open nl socket: %m"); 62 | goto err; 63 | } 64 | 65 | rtnl_event.sock = create_socket(NETLINK_ROUTE); 66 | if (!rtnl_event.sock) { 67 | syslog(LOG_ERR, "Unable to open nl event socket: %m"); 68 | goto err; 69 | } 70 | 71 | rtnl_event.ev.uloop.fd = nl_socket_get_fd(rtnl_event.sock); 72 | 73 | if (nl_socket_set_buffer_size(rtnl_event.sock, rtnl_event.sock_bufsize, 0)) 74 | goto err; 75 | 76 | nl_socket_disable_seq_check(rtnl_event.sock); 77 | 78 | nl_socket_modify_cb(rtnl_event.sock, NL_CB_VALID, NL_CB_CUSTOM, 79 | cb_rtnl_valid, NULL); 80 | 81 | /* Receive IPv4 address, IPv6 address, IPv6 routes and neighbor events */ 82 | if (nl_socket_add_memberships(rtnl_event.sock, RTNLGRP_IPV4_IFADDR, 83 | RTNLGRP_IPV6_IFADDR, RTNLGRP_IPV6_ROUTE, 84 | RTNLGRP_NEIGH, RTNLGRP_LINK, 0)) 85 | goto err; 86 | 87 | odhcpd_register(&rtnl_event.ev); 88 | 89 | return 0; 90 | 91 | err: 92 | if (rtnl_socket) { 93 | nl_socket_free(rtnl_socket); 94 | rtnl_socket = NULL; 95 | } 96 | 97 | if (rtnl_event.sock) { 98 | nl_socket_free(rtnl_event.sock); 99 | rtnl_event.sock = NULL; 100 | rtnl_event.ev.uloop.fd = -1; 101 | } 102 | 103 | return -1; 104 | } 105 | 106 | 107 | int netlink_add_netevent_handler(struct netevent_handler *handler) 108 | { 109 | if (!handler->cb) 110 | return -1; 111 | 112 | list_add(&handler->head, &netevent_handler_list); 113 | 114 | return 0; 115 | } 116 | 117 | static void call_netevent_handler_list(unsigned long event, struct netevent_handler_info *info) 118 | { 119 | struct netevent_handler *handler; 120 | 121 | list_for_each_entry(handler, &netevent_handler_list, head) 122 | handler->cb(event, info); 123 | } 124 | 125 | static void handle_rtnl_event(struct odhcpd_event *e) 126 | { 127 | struct event_socket *ev_sock = container_of(e, struct event_socket, ev); 128 | 129 | nl_recvmsgs_default(ev_sock->sock); 130 | } 131 | 132 | static void refresh_iface_addr4(int ifindex) 133 | { 134 | struct odhcpd_ipaddr *addr = NULL; 135 | struct interface *iface; 136 | ssize_t len = netlink_get_interface_addrs(ifindex, false, &addr); 137 | bool change = false; 138 | 139 | if (len < 0) 140 | return; 141 | 142 | avl_for_each_element(&interfaces, iface, avl) { 143 | struct netevent_handler_info event_info; 144 | 145 | if (iface->ifindex != ifindex) 146 | continue; 147 | 148 | memset(&event_info, 0, sizeof(event_info)); 149 | event_info.iface = iface; 150 | event_info.addrs_old.addrs = iface->addr4; 151 | event_info.addrs_old.len = iface->addr4_len; 152 | 153 | if (!change) { 154 | change = len != (ssize_t)iface->addr4_len; 155 | for (ssize_t i = 0; !change && i < len; ++i) { 156 | if (addr[i].addr.in.s_addr != iface->addr4[i].addr.in.s_addr) 157 | change = true; 158 | } 159 | } 160 | 161 | iface->addr4 = addr; 162 | iface->addr4_len = len; 163 | 164 | if (change) 165 | call_netevent_handler_list(NETEV_ADDRLIST_CHANGE, &event_info); 166 | 167 | free(event_info.addrs_old.addrs); 168 | 169 | if (!len) 170 | continue; 171 | 172 | addr = malloc(len * sizeof(*addr)); 173 | if (!addr) 174 | break; 175 | 176 | memcpy(addr, iface->addr4, len * sizeof(*addr)); 177 | } 178 | 179 | free(addr); 180 | } 181 | 182 | static void refresh_iface_addr6(int ifindex) 183 | { 184 | struct odhcpd_ipaddr *addr = NULL; 185 | struct interface *iface; 186 | ssize_t len = netlink_get_interface_addrs(ifindex, true, &addr); 187 | time_t now = odhcpd_time(); 188 | bool change = false; 189 | 190 | if (len < 0) 191 | return; 192 | 193 | avl_for_each_element(&interfaces, iface, avl) { 194 | struct netevent_handler_info event_info; 195 | 196 | if (iface->ifindex != ifindex) 197 | continue; 198 | 199 | memset(&event_info, 0, sizeof(event_info)); 200 | event_info.iface = iface; 201 | event_info.addrs_old.addrs = iface->addr6; 202 | event_info.addrs_old.len = iface->addr6_len; 203 | 204 | if (!change) { 205 | change = len != (ssize_t)iface->addr6_len; 206 | for (ssize_t i = 0; !change && i < len; ++i) { 207 | if (!IN6_ARE_ADDR_EQUAL(&addr[i].addr.in6, &iface->addr6[i].addr.in6) || 208 | addr[i].prefix != iface->addr6[i].prefix || 209 | (addr[i].preferred_lt > (uint32_t)now) != (iface->addr6[i].preferred_lt > (uint32_t)now) || 210 | addr[i].valid_lt < iface->addr6[i].valid_lt || addr[i].preferred_lt < iface->addr6[i].preferred_lt) 211 | change = true; 212 | } 213 | 214 | if (change) { 215 | /* 216 | * Keep track of removed prefixes, so we could advertise them as invalid 217 | * for at least a couple of times. 218 | * 219 | * RFC7084 § 4.3 : 220 | * L-13: If the delegated prefix changes, i.e., the current prefix is 221 | * replaced with a new prefix without any overlapping time 222 | * period, then the IPv6 CE router MUST immediately advertise the 223 | * old prefix with a Preferred Lifetime of zero and a Valid 224 | * Lifetime of either a) zero or b) the lower of the current 225 | * Valid Lifetime and two hours (which must be decremented in 226 | * real time) in a Router Advertisement message as described in 227 | * Section 5.5.3, (e) of [RFC4862]. 228 | */ 229 | 230 | for (size_t i = 0; i < iface->addr6_len; ++i) { 231 | bool removed = true; 232 | 233 | if (iface->addr6[i].valid_lt <= (uint32_t)now) 234 | continue; 235 | 236 | for (ssize_t j = 0; removed && j < len; ++j) { 237 | size_t plen = min(addr[j].prefix, iface->addr6[i].prefix); 238 | 239 | if (odhcpd_bmemcmp(&addr[j].addr.in6, &iface->addr6[i].addr.in6, plen) == 0) 240 | removed = false; 241 | } 242 | 243 | for (size_t j = 0; removed && j < iface->invalid_addr6_len; ++j) { 244 | size_t plen = min(iface->invalid_addr6[j].prefix, iface->addr6[i].prefix); 245 | 246 | if (odhcpd_bmemcmp(&iface->invalid_addr6[j].addr.in6, &iface->addr6[i].addr.in6, plen) == 0) 247 | removed = false; 248 | } 249 | 250 | if (removed) { 251 | size_t pos = iface->invalid_addr6_len; 252 | struct odhcpd_ipaddr *new_invalid_addr6 = realloc(iface->invalid_addr6, 253 | sizeof(*iface->invalid_addr6) * (pos + 1)); 254 | 255 | if (!new_invalid_addr6) 256 | break; 257 | 258 | iface->invalid_addr6 = new_invalid_addr6; 259 | iface->invalid_addr6_len++; 260 | memcpy(&iface->invalid_addr6[pos], &iface->addr6[i], sizeof(*iface->invalid_addr6)); 261 | iface->invalid_addr6[pos].valid_lt = iface->invalid_addr6[pos].preferred_lt = (uint32_t)now; 262 | 263 | if (iface->invalid_addr6[pos].prefix < 64) 264 | iface->invalid_addr6[pos].prefix = 64; 265 | } 266 | } 267 | } 268 | } 269 | 270 | iface->addr6 = addr; 271 | iface->addr6_len = len; 272 | 273 | if (change) 274 | call_netevent_handler_list(NETEV_ADDR6LIST_CHANGE, &event_info); 275 | 276 | free(event_info.addrs_old.addrs); 277 | 278 | if (!len) 279 | continue; 280 | 281 | addr = malloc(len * sizeof(*addr)); 282 | if (!addr) 283 | break; 284 | 285 | memcpy(addr, iface->addr6, len * sizeof(*addr)); 286 | } 287 | 288 | free(addr); 289 | } 290 | 291 | static int handle_rtm_link(struct nlmsghdr *hdr) 292 | { 293 | struct ifinfomsg *ifi = nlmsg_data(hdr); 294 | struct nlattr *nla[__IFLA_MAX]; 295 | struct interface *iface; 296 | struct netevent_handler_info event_info; 297 | const char *ifname; 298 | 299 | memset(&event_info, 0, sizeof(event_info)); 300 | 301 | if (!nlmsg_valid_hdr(hdr, sizeof(*ifi)) || ifi->ifi_family != AF_UNSPEC) 302 | return NL_SKIP; 303 | 304 | nlmsg_parse(hdr, sizeof(*ifi), nla, __IFLA_MAX - 1, NULL); 305 | if (!nla[IFLA_IFNAME]) 306 | return NL_SKIP; 307 | 308 | ifname = nla_get_string(nla[IFLA_IFNAME]); 309 | 310 | avl_for_each_element(&interfaces, iface, avl) { 311 | if (strcmp(iface->ifname, ifname)) 312 | continue; 313 | 314 | iface->ifflags = ifi->ifi_flags; 315 | 316 | /* 317 | * Assume for link event of the same index, that link changed 318 | * and reload services to enable or disable them based on the 319 | * RUNNING state of the interface. 320 | */ 321 | if (iface->ifindex == ifi->ifi_index) { 322 | reload_services(iface); 323 | continue; 324 | } 325 | 326 | iface->ifindex = ifi->ifi_index; 327 | event_info.iface = iface; 328 | call_netevent_handler_list(NETEV_IFINDEX_CHANGE, &event_info); 329 | } 330 | 331 | return NL_OK; 332 | } 333 | 334 | static int handle_rtm_route(struct nlmsghdr *hdr, bool add) 335 | { 336 | struct rtmsg *rtm = nlmsg_data(hdr); 337 | struct nlattr *nla[__RTA_MAX]; 338 | struct interface *iface; 339 | struct netevent_handler_info event_info; 340 | int ifindex = 0; 341 | 342 | if (!nlmsg_valid_hdr(hdr, sizeof(*rtm)) || rtm->rtm_family != AF_INET6) 343 | return NL_SKIP; 344 | 345 | nlmsg_parse(hdr, sizeof(*rtm), nla, __RTA_MAX - 1, NULL); 346 | 347 | memset(&event_info, 0, sizeof(event_info)); 348 | event_info.rt.dst_len = rtm->rtm_dst_len; 349 | 350 | if (nla[RTA_DST]) 351 | nla_memcpy(&event_info.rt.dst, nla[RTA_DST], 352 | sizeof(event_info.rt.dst)); 353 | 354 | if (nla[RTA_OIF]) 355 | ifindex = nla_get_u32(nla[RTA_OIF]); 356 | 357 | if (nla[RTA_GATEWAY]) 358 | nla_memcpy(&event_info.rt.gateway, nla[RTA_GATEWAY], 359 | sizeof(event_info.rt.gateway)); 360 | 361 | avl_for_each_element(&interfaces, iface, avl) { 362 | if (ifindex && iface->ifindex != ifindex) 363 | continue; 364 | 365 | event_info.iface = ifindex ? iface : NULL; 366 | call_netevent_handler_list(add ? NETEV_ROUTE6_ADD : NETEV_ROUTE6_DEL, 367 | &event_info); 368 | } 369 | 370 | return NL_OK; 371 | } 372 | 373 | static int handle_rtm_addr(struct nlmsghdr *hdr, bool add) 374 | { 375 | struct ifaddrmsg *ifa = nlmsg_data(hdr); 376 | struct nlattr *nla[__IFA_MAX]; 377 | struct interface *iface; 378 | struct netevent_handler_info event_info; 379 | char buf[INET6_ADDRSTRLEN]; 380 | 381 | if (!nlmsg_valid_hdr(hdr, sizeof(*ifa)) || 382 | (ifa->ifa_family != AF_INET6 && 383 | ifa->ifa_family != AF_INET)) 384 | return NL_SKIP; 385 | 386 | memset(&event_info, 0, sizeof(event_info)); 387 | 388 | nlmsg_parse(hdr, sizeof(*ifa), nla, __IFA_MAX - 1, NULL); 389 | 390 | if (ifa->ifa_family == AF_INET6) { 391 | if (!nla[IFA_ADDRESS]) 392 | return NL_SKIP; 393 | 394 | nla_memcpy(&event_info.addr, nla[IFA_ADDRESS], sizeof(event_info.addr)); 395 | 396 | if (IN6_IS_ADDR_MULTICAST(&event_info.addr)) 397 | return NL_SKIP; 398 | 399 | inet_ntop(AF_INET6, &event_info.addr, buf, sizeof(buf)); 400 | 401 | avl_for_each_element(&interfaces, iface, avl) { 402 | if (iface->ifindex != (int)ifa->ifa_index) 403 | continue; 404 | 405 | if (add && IN6_IS_ADDR_LINKLOCAL(&event_info.addr)) { 406 | iface->have_link_local = true; 407 | return NL_SKIP; 408 | } 409 | 410 | syslog(LOG_DEBUG, "Netlink %s %s on %s", add ? "newaddr" : "deladdr", 411 | buf, iface->name); 412 | 413 | event_info.iface = iface; 414 | call_netevent_handler_list(add ? NETEV_ADDR6_ADD : NETEV_ADDR6_DEL, 415 | &event_info); 416 | } 417 | 418 | refresh_iface_addr6(ifa->ifa_index); 419 | } else { 420 | if (!nla[IFA_LOCAL]) 421 | return NL_SKIP; 422 | 423 | nla_memcpy(&event_info.addr, nla[IFA_LOCAL], sizeof(event_info.addr)); 424 | 425 | inet_ntop(AF_INET, &event_info.addr, buf, sizeof(buf)); 426 | 427 | avl_for_each_element(&interfaces, iface, avl) { 428 | if (iface->ifindex != (int)ifa->ifa_index) 429 | continue; 430 | 431 | syslog(LOG_DEBUG, "Netlink %s %s on %s", add ? "newaddr" : "deladdr", 432 | buf, iface->name); 433 | 434 | event_info.iface = iface; 435 | call_netevent_handler_list(add ? NETEV_ADDR_ADD : NETEV_ADDR_DEL, 436 | &event_info); 437 | } 438 | 439 | refresh_iface_addr4(ifa->ifa_index); 440 | } 441 | 442 | return NL_OK; 443 | } 444 | 445 | static int handle_rtm_neigh(struct nlmsghdr *hdr, bool add) 446 | { 447 | struct ndmsg *ndm = nlmsg_data(hdr); 448 | struct nlattr *nla[__NDA_MAX]; 449 | struct interface *iface; 450 | struct netevent_handler_info event_info; 451 | char buf[INET6_ADDRSTRLEN]; 452 | 453 | if (!nlmsg_valid_hdr(hdr, sizeof(*ndm)) || 454 | ndm->ndm_family != AF_INET6) 455 | return NL_SKIP; 456 | 457 | nlmsg_parse(hdr, sizeof(*ndm), nla, __NDA_MAX - 1, NULL); 458 | if (!nla[NDA_DST]) 459 | return NL_SKIP; 460 | 461 | memset(&event_info, 0, sizeof(event_info)); 462 | 463 | nla_memcpy(&event_info.neigh.dst, nla[NDA_DST], sizeof(event_info.neigh.dst)); 464 | 465 | if (IN6_IS_ADDR_LINKLOCAL(&event_info.neigh.dst) || 466 | IN6_IS_ADDR_MULTICAST(&event_info.neigh.dst)) 467 | return NL_SKIP; 468 | 469 | inet_ntop(AF_INET6, &event_info.neigh.dst, buf, sizeof(buf)); 470 | 471 | avl_for_each_element(&interfaces, iface, avl) { 472 | if (iface->ifindex != ndm->ndm_ifindex) 473 | continue; 474 | 475 | syslog(LOG_DEBUG, "Netlink %s %s on %s", true ? "newneigh" : "delneigh", 476 | buf, iface->name); 477 | 478 | event_info.iface = iface; 479 | event_info.neigh.state = ndm->ndm_state; 480 | event_info.neigh.flags = ndm->ndm_flags; 481 | 482 | call_netevent_handler_list(add ? NETEV_NEIGH6_ADD : NETEV_NEIGH6_DEL, 483 | &event_info); 484 | } 485 | 486 | return NL_OK; 487 | } 488 | 489 | /* Handler for neighbor cache entries from the kernel. This is our source 490 | * to learn and unlearn hosts on interfaces. */ 491 | static int cb_rtnl_valid(struct nl_msg *msg, _unused void *arg) 492 | { 493 | struct nlmsghdr *hdr = nlmsg_hdr(msg); 494 | int ret = NL_SKIP; 495 | bool add = false; 496 | 497 | switch (hdr->nlmsg_type) { 498 | case RTM_NEWLINK: 499 | ret = handle_rtm_link(hdr); 500 | break; 501 | 502 | case RTM_NEWROUTE: 503 | add = true; 504 | /* fall through */ 505 | case RTM_DELROUTE: 506 | ret = handle_rtm_route(hdr, add); 507 | break; 508 | 509 | case RTM_NEWADDR: 510 | add = true; 511 | /* fall through */ 512 | case RTM_DELADDR: 513 | ret = handle_rtm_addr(hdr, add); 514 | break; 515 | 516 | case RTM_NEWNEIGH: 517 | add = true; 518 | /* fall through */ 519 | case RTM_DELNEIGH: 520 | ret = handle_rtm_neigh(hdr, add); 521 | break; 522 | 523 | default: 524 | break; 525 | } 526 | 527 | return ret; 528 | } 529 | 530 | static void catch_rtnl_err(struct odhcpd_event *e, int error) 531 | { 532 | struct event_socket *ev_sock = container_of(e, struct event_socket, ev); 533 | 534 | if (error != ENOBUFS) 535 | goto err; 536 | 537 | /* Double netlink event buffer size */ 538 | ev_sock->sock_bufsize *= 2; 539 | 540 | if (nl_socket_set_buffer_size(ev_sock->sock, ev_sock->sock_bufsize, 0)) 541 | goto err; 542 | 543 | netlink_dump_addr_table(true); 544 | return; 545 | 546 | err: 547 | odhcpd_deregister(e); 548 | } 549 | 550 | static struct nl_sock *create_socket(int protocol) 551 | { 552 | struct nl_sock *nl_sock; 553 | 554 | nl_sock = nl_socket_alloc(); 555 | if (!nl_sock) 556 | goto err; 557 | 558 | if (nl_connect(nl_sock, protocol) < 0) 559 | goto err; 560 | 561 | return nl_sock; 562 | 563 | err: 564 | if (nl_sock) 565 | nl_socket_free(nl_sock); 566 | 567 | return NULL; 568 | } 569 | 570 | 571 | struct addr_info { 572 | int ifindex; 573 | int af; 574 | struct odhcpd_ipaddr **addrs; 575 | int pending; 576 | ssize_t ret; 577 | }; 578 | 579 | 580 | static int cb_addr_valid(struct nl_msg *msg, void *arg) 581 | { 582 | struct addr_info *ctxt = (struct addr_info *)arg; 583 | struct odhcpd_ipaddr *addrs = *(ctxt->addrs); 584 | struct nlmsghdr *hdr = nlmsg_hdr(msg); 585 | struct ifaddrmsg *ifa; 586 | struct nlattr *nla[__IFA_MAX], *nla_addr = NULL; 587 | 588 | if (hdr->nlmsg_type != RTM_NEWADDR) 589 | return NL_SKIP; 590 | 591 | ifa = NLMSG_DATA(hdr); 592 | if (ifa->ifa_scope != RT_SCOPE_UNIVERSE || 593 | (ctxt->af != ifa->ifa_family) || 594 | (ctxt->ifindex && ifa->ifa_index != (unsigned)ctxt->ifindex)) 595 | return NL_SKIP; 596 | 597 | nlmsg_parse(hdr, sizeof(*ifa), nla, __IFA_MAX - 1, NULL); 598 | 599 | switch (ifa->ifa_family) { 600 | case AF_INET6: 601 | if (nla[IFA_ADDRESS]) 602 | nla_addr = nla[IFA_ADDRESS]; 603 | break; 604 | 605 | case AF_INET: 606 | if (nla[IFA_LOCAL]) 607 | nla_addr = nla[IFA_LOCAL]; 608 | break; 609 | 610 | default: 611 | break; 612 | } 613 | if (!nla_addr) 614 | return NL_SKIP; 615 | 616 | addrs = realloc(addrs, sizeof(*addrs)*(ctxt->ret + 1)); 617 | if (!addrs) 618 | return NL_SKIP; 619 | 620 | memset(&addrs[ctxt->ret], 0, sizeof(addrs[ctxt->ret])); 621 | addrs[ctxt->ret].prefix = ifa->ifa_prefixlen; 622 | 623 | nla_memcpy(&addrs[ctxt->ret].addr, nla_addr, 624 | sizeof(addrs[ctxt->ret].addr)); 625 | 626 | if (nla[IFA_BROADCAST]) 627 | nla_memcpy(&addrs[ctxt->ret].broadcast, nla[IFA_BROADCAST], 628 | sizeof(addrs[ctxt->ret].broadcast)); 629 | 630 | if (nla[IFA_CACHEINFO]) { 631 | struct ifa_cacheinfo *ifc = nla_data(nla[IFA_CACHEINFO]); 632 | 633 | addrs[ctxt->ret].preferred_lt = ifc->ifa_prefered; 634 | addrs[ctxt->ret].valid_lt = ifc->ifa_valid; 635 | } 636 | 637 | if (ifa->ifa_flags & IFA_F_DEPRECATED) 638 | addrs[ctxt->ret].preferred_lt = 0; 639 | 640 | if (ifa->ifa_family == AF_INET6 && 641 | ifa->ifa_flags & IFA_F_TENTATIVE) 642 | addrs[ctxt->ret].tentative = true; 643 | 644 | ctxt->ret++; 645 | *(ctxt->addrs) = addrs; 646 | 647 | return NL_OK; 648 | } 649 | 650 | 651 | static int cb_addr_finish(_unused struct nl_msg *msg, void *arg) 652 | { 653 | struct addr_info *ctxt = (struct addr_info *)arg; 654 | 655 | ctxt->pending = 0; 656 | 657 | return NL_STOP; 658 | } 659 | 660 | 661 | static int cb_addr_error(_unused struct sockaddr_nl *nla, struct nlmsgerr *err, 662 | void *arg) 663 | { 664 | struct addr_info *ctxt = (struct addr_info *)arg; 665 | 666 | ctxt->pending = 0; 667 | ctxt->ret = err->error; 668 | 669 | return NL_STOP; 670 | } 671 | 672 | 673 | static int prefix_cmp(const void *va, const void *vb) 674 | { 675 | const struct odhcpd_ipaddr *a = va, *b = vb; 676 | int ret = 0; 677 | 678 | if (a->prefix == b->prefix) { 679 | ret = (ntohl(a->addr.in.s_addr) < ntohl(b->addr.in.s_addr)) ? 1 : 680 | (ntohl(a->addr.in.s_addr) > ntohl(b->addr.in.s_addr)) ? -1 : 0; 681 | } else 682 | ret = a->prefix < b->prefix ? 1 : -1; 683 | 684 | return ret; 685 | } 686 | 687 | 688 | /* compare IPv6 prefixes */ 689 | static int prefix6_cmp(const void *va, const void *vb) 690 | { 691 | const struct odhcpd_ipaddr *a = va, *b = vb; 692 | uint32_t a_pref_lt = IN6_IS_ADDR_ULA(&a->addr.in6) ? 1 : a->preferred_lt; 693 | uint32_t b_pref_lt = IN6_IS_ADDR_ULA(&b->addr.in6) ? 1 : b->preferred_lt; 694 | return (a_pref_lt < b_pref_lt) ? 1 : (a_pref_lt > b_pref_lt) ? -1 : 0; 695 | } 696 | 697 | 698 | /* Detect an IPV6-address currently assigned to the given interface */ 699 | ssize_t netlink_get_interface_addrs(int ifindex, bool v6, struct odhcpd_ipaddr **addrs) 700 | { 701 | struct nl_msg *msg; 702 | struct ifaddrmsg ifa = { 703 | .ifa_family = v6? AF_INET6: AF_INET, 704 | .ifa_prefixlen = 0, 705 | .ifa_flags = 0, 706 | .ifa_scope = 0, 707 | .ifa_index = ifindex, }; 708 | struct nl_cb *cb = nl_cb_alloc(NL_CB_DEFAULT); 709 | struct addr_info ctxt = { 710 | .ifindex = ifindex, 711 | .af = v6? AF_INET6: AF_INET, 712 | .addrs = addrs, 713 | .ret = 0, 714 | .pending = 1, 715 | }; 716 | 717 | if (!cb) { 718 | ctxt.ret = -1; 719 | goto out; 720 | } 721 | 722 | msg = nlmsg_alloc_simple(RTM_GETADDR, NLM_F_REQUEST | NLM_F_DUMP); 723 | 724 | if (!msg) { 725 | ctxt.ret = - 1; 726 | goto out; 727 | } 728 | 729 | nlmsg_append(msg, &ifa, sizeof(ifa), 0); 730 | 731 | nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, cb_addr_valid, &ctxt); 732 | nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, cb_addr_finish, &ctxt); 733 | nl_cb_err(cb, NL_CB_CUSTOM, cb_addr_error, &ctxt); 734 | 735 | ctxt.ret = nl_send_auto_complete(rtnl_socket, msg); 736 | if (ctxt.ret < 0) 737 | goto free; 738 | 739 | ctxt.ret = 0; 740 | while (ctxt.pending > 0) 741 | nl_recvmsgs(rtnl_socket, cb); 742 | 743 | if (ctxt.ret <= 0) 744 | goto free; 745 | 746 | time_t now = odhcpd_time(); 747 | struct odhcpd_ipaddr *addr = *addrs; 748 | 749 | qsort(addr, ctxt.ret, sizeof(*addr), v6 ? prefix6_cmp : prefix_cmp); 750 | 751 | for (ssize_t i = 0; i < ctxt.ret; ++i) { 752 | if (addr[i].preferred_lt < UINT32_MAX - now) 753 | addr[i].preferred_lt += now; 754 | 755 | if (addr[i].valid_lt < UINT32_MAX - now) 756 | addr[i].valid_lt += now; 757 | } 758 | 759 | free: 760 | nlmsg_free(msg); 761 | out: 762 | nl_cb_put(cb); 763 | 764 | return ctxt.ret; 765 | } 766 | 767 | 768 | static int cb_linklocal_valid(struct nl_msg *msg, void *arg) 769 | { 770 | struct addr_info *ctxt = (struct addr_info *)arg; 771 | struct odhcpd_ipaddr *addrs = *(ctxt->addrs); 772 | struct nlmsghdr *hdr = nlmsg_hdr(msg); 773 | struct ifaddrmsg *ifa; 774 | struct nlattr *nla[__IFA_MAX], *nla_addr = NULL; 775 | struct in6_addr addr; 776 | 777 | if (hdr->nlmsg_type != RTM_NEWADDR) 778 | return NL_SKIP; 779 | 780 | ifa = NLMSG_DATA(hdr); 781 | if (ifa->ifa_scope != RT_SCOPE_LINK || 782 | (ctxt->af != ifa->ifa_family) || 783 | (ctxt->ifindex && ifa->ifa_index != (unsigned)ctxt->ifindex)) 784 | return NL_SKIP; 785 | 786 | nlmsg_parse(hdr, sizeof(*ifa), nla, __IFA_MAX - 1, NULL); 787 | 788 | switch (ifa->ifa_family) { 789 | case AF_INET6: 790 | if (nla[IFA_ADDRESS]) 791 | nla_addr = nla[IFA_ADDRESS]; 792 | break; 793 | 794 | default: 795 | break; 796 | } 797 | if (!nla_addr) 798 | return NL_SKIP; 799 | 800 | nla_memcpy(&addr, nla_addr, sizeof(addr)); 801 | 802 | if (!IN6_IS_ADDR_LINKLOCAL(&addr)) 803 | return NL_SKIP; 804 | 805 | addrs = realloc(addrs, sizeof(*addrs)*(ctxt->ret + 1)); 806 | if (!addrs) 807 | return NL_SKIP; 808 | 809 | memset(&addrs[ctxt->ret], 0, sizeof(addrs[ctxt->ret])); 810 | memcpy(&addrs[ctxt->ret].addr, &addr, sizeof(addrs[ctxt->ret].addr)); 811 | 812 | if (ifa->ifa_flags & IFA_F_TENTATIVE) 813 | addrs[ctxt->ret].tentative = true; 814 | 815 | ctxt->ret++; 816 | *(ctxt->addrs) = addrs; 817 | 818 | return NL_OK; 819 | } 820 | 821 | 822 | static int cb_linklocal_finish(_unused struct nl_msg *msg, void *arg) 823 | { 824 | struct addr_info *ctxt = (struct addr_info *)arg; 825 | 826 | ctxt->pending = 0; 827 | 828 | return NL_STOP; 829 | } 830 | 831 | 832 | static int cb_linklocal_error(_unused struct sockaddr_nl *nla, struct nlmsgerr *err, 833 | void *arg) 834 | { 835 | struct addr_info *ctxt = (struct addr_info *)arg; 836 | 837 | ctxt->pending = 0; 838 | ctxt->ret = err->error; 839 | 840 | return NL_STOP; 841 | } 842 | 843 | 844 | /* Detect a link local IPV6-address currently assigned to the given interface */ 845 | ssize_t netlink_get_interface_linklocal(int ifindex, struct odhcpd_ipaddr **addrs) 846 | { 847 | struct nl_msg *msg; 848 | struct ifaddrmsg ifa = { 849 | .ifa_family = AF_INET6, 850 | .ifa_prefixlen = 0, 851 | .ifa_flags = 0, 852 | .ifa_scope = 0, 853 | .ifa_index = ifindex, }; 854 | struct nl_cb *cb = nl_cb_alloc(NL_CB_DEFAULT); 855 | struct addr_info ctxt = { 856 | .ifindex = ifindex, 857 | .af = AF_INET6, 858 | .addrs = addrs, 859 | .ret = 0, 860 | .pending = 1, 861 | }; 862 | 863 | if (!cb) { 864 | ctxt.ret = -1; 865 | goto out; 866 | } 867 | 868 | msg = nlmsg_alloc_simple(RTM_GETADDR, NLM_F_REQUEST | NLM_F_DUMP); 869 | 870 | if (!msg) { 871 | ctxt.ret = - 1; 872 | goto out; 873 | } 874 | 875 | nlmsg_append(msg, &ifa, sizeof(ifa), 0); 876 | 877 | nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, cb_linklocal_valid, &ctxt); 878 | nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, cb_linklocal_finish, &ctxt); 879 | nl_cb_err(cb, NL_CB_CUSTOM, cb_linklocal_error, &ctxt); 880 | 881 | ctxt.ret = nl_send_auto_complete(rtnl_socket, msg); 882 | if (ctxt.ret < 0) 883 | goto free; 884 | 885 | ctxt.ret = 0; 886 | while (ctxt.pending > 0) 887 | nl_recvmsgs(rtnl_socket, cb); 888 | 889 | if (ctxt.ret <= 0) 890 | goto free; 891 | 892 | free: 893 | nlmsg_free(msg); 894 | out: 895 | nl_cb_put(cb); 896 | 897 | return ctxt.ret; 898 | } 899 | 900 | 901 | struct neigh_info { 902 | int ifindex; 903 | int pending; 904 | const struct in6_addr *addr; 905 | int ret; 906 | }; 907 | 908 | 909 | static int cb_proxy_neigh_valid(struct nl_msg *msg, void *arg) 910 | { 911 | struct neigh_info *ctxt = (struct neigh_info *)arg; 912 | struct nlmsghdr *hdr = nlmsg_hdr(msg); 913 | struct ndmsg *ndm; 914 | struct nlattr *nla_dst; 915 | 916 | if (hdr->nlmsg_type != RTM_NEWNEIGH) 917 | return NL_SKIP; 918 | 919 | ndm = NLMSG_DATA(hdr); 920 | if (ndm->ndm_family != AF_INET6 || 921 | (ctxt->ifindex && ndm->ndm_ifindex != ctxt->ifindex)) 922 | return NL_SKIP; 923 | 924 | if (!(ndm->ndm_flags & NTF_PROXY)) 925 | return NL_SKIP; 926 | 927 | nla_dst = nlmsg_find_attr(hdr, sizeof(*ndm), NDA_DST); 928 | if (!nla_dst) 929 | return NL_SKIP; 930 | 931 | if (nla_memcmp(nla_dst,ctxt->addr, 16) == 0) 932 | ctxt->ret = 1; 933 | 934 | return NL_OK; 935 | } 936 | 937 | 938 | static int cb_proxy_neigh_finish(_unused struct nl_msg *msg, void *arg) 939 | { 940 | struct neigh_info *ctxt = (struct neigh_info *)arg; 941 | 942 | ctxt->pending = 0; 943 | 944 | return NL_STOP; 945 | } 946 | 947 | 948 | static int cb_proxy_neigh_error(_unused struct sockaddr_nl *nla, struct nlmsgerr *err, 949 | void *arg) 950 | { 951 | struct neigh_info *ctxt = (struct neigh_info *)arg; 952 | 953 | ctxt->pending = 0; 954 | ctxt->ret = err->error; 955 | 956 | return NL_STOP; 957 | } 958 | 959 | /* Detect an IPV6-address proxy neighbor for the given interface */ 960 | int netlink_get_interface_proxy_neigh(int ifindex, const struct in6_addr *addr) 961 | { 962 | struct nl_msg *msg; 963 | struct ndmsg ndm = { 964 | .ndm_family = AF_INET6, 965 | .ndm_flags = NTF_PROXY, 966 | .ndm_ifindex = ifindex, 967 | }; 968 | struct nl_cb *cb = nl_cb_alloc(NL_CB_DEFAULT); 969 | struct neigh_info ctxt = { 970 | .ifindex = ifindex, 971 | .addr = addr, 972 | .ret = 0, 973 | .pending = 1, 974 | }; 975 | 976 | if (!cb) { 977 | ctxt.ret = -1; 978 | goto out; 979 | } 980 | 981 | msg = nlmsg_alloc_simple(RTM_GETNEIGH, NLM_F_REQUEST | NLM_F_MATCH); 982 | 983 | if (!msg) { 984 | ctxt.ret = -1; 985 | goto out; 986 | } 987 | 988 | nlmsg_append(msg, &ndm, sizeof(ndm), 0); 989 | nla_put(msg, NDA_DST, sizeof(*addr), addr); 990 | 991 | nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, cb_proxy_neigh_valid, &ctxt); 992 | nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, cb_proxy_neigh_finish, &ctxt); 993 | nl_cb_err(cb, NL_CB_CUSTOM, cb_proxy_neigh_error, &ctxt); 994 | 995 | ctxt.ret = nl_send_auto_complete(rtnl_socket, msg); 996 | if (ctxt.ret < 0) 997 | goto free; 998 | 999 | while (ctxt.pending > 0) 1000 | nl_recvmsgs(rtnl_socket, cb); 1001 | 1002 | free: 1003 | nlmsg_free(msg); 1004 | out: 1005 | nl_cb_put(cb); 1006 | 1007 | return ctxt.ret; 1008 | } 1009 | 1010 | 1011 | int netlink_setup_route(const struct in6_addr *addr, const int prefixlen, 1012 | const int ifindex, const struct in6_addr *gw, 1013 | const uint32_t metric, const bool add) 1014 | { 1015 | struct nl_msg *msg; 1016 | struct rtmsg rtm = { 1017 | .rtm_family = AF_INET6, 1018 | .rtm_dst_len = prefixlen, 1019 | .rtm_src_len = 0, 1020 | .rtm_table = RT_TABLE_MAIN, 1021 | .rtm_protocol = (add ? RTPROT_STATIC : RTPROT_UNSPEC), 1022 | .rtm_scope = (add ? (gw ? RT_SCOPE_UNIVERSE : RT_SCOPE_LINK) : RT_SCOPE_NOWHERE), 1023 | .rtm_type = (add ? RTN_UNICAST : RTN_UNSPEC), 1024 | }; 1025 | int ret = 0; 1026 | 1027 | msg = nlmsg_alloc_simple(add ? RTM_NEWROUTE : RTM_DELROUTE, 1028 | add ? NLM_F_CREATE | NLM_F_REPLACE : 0); 1029 | if (!msg) 1030 | return -1; 1031 | 1032 | nlmsg_append(msg, &rtm, sizeof(rtm), 0); 1033 | 1034 | nla_put(msg, RTA_DST, sizeof(*addr), addr); 1035 | nla_put_u32(msg, RTA_OIF, ifindex); 1036 | nla_put_u32(msg, RTA_PRIORITY, metric); 1037 | 1038 | if (gw) 1039 | nla_put(msg, RTA_GATEWAY, sizeof(*gw), gw); 1040 | 1041 | ret = nl_send_auto_complete(rtnl_socket, msg); 1042 | nlmsg_free(msg); 1043 | 1044 | if (ret < 0) 1045 | return ret; 1046 | 1047 | return nl_wait_for_ack(rtnl_socket); 1048 | } 1049 | 1050 | 1051 | int netlink_setup_proxy_neigh(const struct in6_addr *addr, 1052 | const int ifindex, const bool add) 1053 | { 1054 | struct nl_msg *msg; 1055 | struct ndmsg ndm = { 1056 | .ndm_family = AF_INET6, 1057 | .ndm_flags = NTF_PROXY, 1058 | .ndm_ifindex = ifindex, 1059 | }; 1060 | int ret = 0, flags = NLM_F_REQUEST; 1061 | 1062 | if (add) 1063 | flags |= NLM_F_REPLACE | NLM_F_CREATE; 1064 | 1065 | msg = nlmsg_alloc_simple(add ? RTM_NEWNEIGH : RTM_DELNEIGH, flags); 1066 | if (!msg) 1067 | return -1; 1068 | 1069 | nlmsg_append(msg, &ndm, sizeof(ndm), 0); 1070 | 1071 | nla_put(msg, NDA_DST, sizeof(*addr), addr); 1072 | 1073 | ret = nl_send_auto_complete(rtnl_socket, msg); 1074 | nlmsg_free(msg); 1075 | 1076 | if (ret < 0) 1077 | return ret; 1078 | 1079 | return nl_wait_for_ack(rtnl_socket); 1080 | } 1081 | 1082 | 1083 | int netlink_setup_addr(struct odhcpd_ipaddr *addr, 1084 | const int ifindex, const bool v6, const bool add) 1085 | { 1086 | struct nl_msg *msg; 1087 | struct ifaddrmsg ifa = { 1088 | .ifa_family = v6 ? AF_INET6 : AF_INET, 1089 | .ifa_prefixlen = addr->prefix, 1090 | .ifa_flags = 0, 1091 | .ifa_scope = 0, 1092 | .ifa_index = ifindex, }; 1093 | int ret = 0, flags = NLM_F_REQUEST; 1094 | 1095 | if (add) 1096 | flags |= NLM_F_REPLACE | NLM_F_CREATE; 1097 | 1098 | msg = nlmsg_alloc_simple(add ? RTM_NEWADDR : RTM_DELADDR, 0); 1099 | if (!msg) 1100 | return -1; 1101 | 1102 | nlmsg_append(msg, &ifa, sizeof(ifa), flags); 1103 | nla_put(msg, IFA_LOCAL, v6 ? 16 : 4, &addr->addr); 1104 | if (v6) { 1105 | struct ifa_cacheinfo cinfo = { .ifa_prefered = 0xffffffffU, 1106 | .ifa_valid = 0xffffffffU, 1107 | .cstamp = 0, 1108 | .tstamp = 0 }; 1109 | time_t now = odhcpd_time(); 1110 | 1111 | if (addr->preferred_lt) { 1112 | int64_t preferred_lt = addr->preferred_lt - now; 1113 | if (preferred_lt < 0) 1114 | preferred_lt = 0; 1115 | else if (preferred_lt > UINT32_MAX) 1116 | preferred_lt = UINT32_MAX; 1117 | 1118 | cinfo.ifa_prefered = preferred_lt; 1119 | } 1120 | 1121 | if (addr->valid_lt) { 1122 | int64_t valid_lt = addr->valid_lt - now; 1123 | if (valid_lt <= 0) { 1124 | nlmsg_free(msg); 1125 | return -1; 1126 | } 1127 | else if (valid_lt > UINT32_MAX) 1128 | valid_lt = UINT32_MAX; 1129 | 1130 | cinfo.ifa_valid = valid_lt; 1131 | } 1132 | 1133 | nla_put(msg, IFA_CACHEINFO, sizeof(cinfo), &cinfo); 1134 | 1135 | nla_put_u32(msg, IFA_FLAGS, IFA_F_NOPREFIXROUTE); 1136 | } else { 1137 | if (addr->broadcast.s_addr) 1138 | nla_put_u32(msg, IFA_BROADCAST, addr->broadcast.s_addr); 1139 | } 1140 | 1141 | ret = nl_send_auto_complete(rtnl_socket, msg); 1142 | nlmsg_free(msg); 1143 | 1144 | if (ret < 0) 1145 | return ret; 1146 | 1147 | return nl_wait_for_ack(rtnl_socket); 1148 | } 1149 | 1150 | void netlink_dump_neigh_table(const bool proxy) 1151 | { 1152 | struct nl_msg *msg; 1153 | struct ndmsg ndm = { 1154 | .ndm_family = AF_INET6, 1155 | .ndm_flags = proxy ? NTF_PROXY : 0, 1156 | }; 1157 | 1158 | msg = nlmsg_alloc_simple(RTM_GETNEIGH, NLM_F_REQUEST | NLM_F_DUMP); 1159 | if (!msg) 1160 | return; 1161 | 1162 | nlmsg_append(msg, &ndm, sizeof(ndm), 0); 1163 | 1164 | nl_send_auto_complete(rtnl_event.sock, msg); 1165 | 1166 | nlmsg_free(msg); 1167 | } 1168 | 1169 | void netlink_dump_addr_table(const bool v6) 1170 | { 1171 | struct nl_msg *msg; 1172 | struct ifaddrmsg ifa = { 1173 | .ifa_family = v6 ? AF_INET6 : AF_INET, 1174 | }; 1175 | 1176 | msg = nlmsg_alloc_simple(RTM_GETADDR, NLM_F_REQUEST | NLM_F_DUMP); 1177 | if (!msg) 1178 | return; 1179 | 1180 | nlmsg_append(msg, &ifa, sizeof(ifa), 0); 1181 | 1182 | nl_send_auto_complete(rtnl_event.sock, msg); 1183 | 1184 | nlmsg_free(msg); 1185 | } 1186 | -------------------------------------------------------------------------------- /src/odhcpd.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2012-2013 Steven Barth 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License v2 as published by 6 | * the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | * 13 | */ 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | 44 | #include 45 | #include "odhcpd.h" 46 | 47 | static int ioctl_sock = -1; 48 | static int urandom_fd = -1; 49 | 50 | static void sighandler(_unused int signal) 51 | { 52 | uloop_end(); 53 | } 54 | 55 | static void print_usage(const char *app) 56 | { 57 | printf( 58 | "== %s Usage ==\n\n" 59 | " -h, --help Print this help\n" 60 | " -l level Specify log level 0..7 (default %d)\n", 61 | app, config.log_level 62 | ); 63 | } 64 | 65 | static bool ipv6_enabled(void) 66 | { 67 | int fd = socket(AF_INET6, SOCK_DGRAM, 0); 68 | 69 | if (fd < 0) 70 | return false; 71 | 72 | close(fd); 73 | 74 | return true; 75 | } 76 | 77 | int main(int argc, char **argv) 78 | { 79 | openlog("odhcpd", LOG_PERROR | LOG_PID, LOG_DAEMON); 80 | int opt; 81 | 82 | while ((opt = getopt(argc, argv, "hl:")) != -1) { 83 | switch (opt) { 84 | case 'h': 85 | print_usage(argv[0]); 86 | return 0; 87 | case 'l': 88 | config.log_level = (atoi(optarg) & LOG_PRIMASK); 89 | fprintf(stderr, "Log level set to %d\n", config.log_level); 90 | break; 91 | } 92 | } 93 | setlogmask(LOG_UPTO(config.log_level)); 94 | uloop_init(); 95 | 96 | if (getuid() != 0) { 97 | syslog(LOG_ERR, "Must be run as root!"); 98 | return 2; 99 | } 100 | 101 | ioctl_sock = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); 102 | 103 | if (ioctl_sock < 0) 104 | return 4; 105 | 106 | if ((urandom_fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC)) < 0) 107 | return 4; 108 | 109 | signal(SIGUSR1, SIG_IGN); 110 | signal(SIGINT, sighandler); 111 | signal(SIGTERM, sighandler); 112 | 113 | if (netlink_init()) 114 | return 4; 115 | 116 | if (ipv6_enabled()) { 117 | if (router_init()) 118 | return 4; 119 | 120 | if (dhcpv6_init()) 121 | return 4; 122 | 123 | if (ndp_init()) 124 | return 4; 125 | } 126 | #ifndef DHCPV4_SUPPORT 127 | else 128 | return 4; 129 | #else 130 | if (dhcpv4_init()) 131 | return 4; 132 | #endif 133 | 134 | odhcpd_run(); 135 | return 0; 136 | } 137 | 138 | 139 | /* Read IPv6 MTU for interface */ 140 | int odhcpd_get_interface_config(const char *ifname, const char *what) 141 | { 142 | char buf[64]; 143 | 144 | snprintf(buf, sizeof(buf), "/proc/sys/net/ipv6/conf/%s/%s", ifname, what); 145 | 146 | int fd = open(buf, O_RDONLY); 147 | if (fd < 0) 148 | return -1; 149 | 150 | ssize_t len = read(fd, buf, sizeof(buf) - 1); 151 | close(fd); 152 | 153 | if (len < 0) 154 | return -1; 155 | 156 | buf[len] = 0; 157 | return atoi(buf); 158 | } 159 | 160 | 161 | /* Read IPv6 MAC for interface */ 162 | int odhcpd_get_mac(const struct interface *iface, uint8_t mac[6]) 163 | { 164 | struct ifreq ifr; 165 | 166 | memset(&ifr, 0, sizeof(ifr)); 167 | strncpy(ifr.ifr_name, iface->ifname, sizeof(ifr.ifr_name) - 1); 168 | if (ioctl(ioctl_sock, SIOCGIFHWADDR, &ifr) < 0) 169 | return -1; 170 | 171 | memcpy(mac, ifr.ifr_hwaddr.sa_data, 6); 172 | return 0; 173 | } 174 | 175 | int odhcpd_get_flags(const struct interface *iface) 176 | { 177 | struct ifreq ifr; 178 | 179 | memset(&ifr, 0, sizeof(ifr)); 180 | strncpy(ifr.ifr_name, iface->ifname, sizeof(ifr.ifr_name) - 1); 181 | if (ioctl(ioctl_sock, SIOCGIFFLAGS, &ifr) < 0) 182 | return -1; 183 | 184 | return ifr.ifr_flags; 185 | } 186 | 187 | 188 | /* Forwards a packet on a specific interface */ 189 | ssize_t odhcpd_send(int socket, struct sockaddr_in6 *dest, 190 | struct iovec *iov, size_t iov_len, 191 | const struct interface *iface) 192 | { 193 | /* Construct headers */ 194 | uint8_t cmsg_buf[CMSG_SPACE(sizeof(struct in6_pktinfo))] = {0}; 195 | struct msghdr msg = { 196 | .msg_name = (void *) dest, 197 | .msg_namelen = sizeof(*dest), 198 | .msg_iov = iov, 199 | .msg_iovlen = iov_len, 200 | .msg_control = cmsg_buf, 201 | .msg_controllen = sizeof(cmsg_buf), 202 | .msg_flags = 0 203 | }; 204 | 205 | /* Set control data (define destination interface) */ 206 | struct cmsghdr *chdr = CMSG_FIRSTHDR(&msg); 207 | chdr->cmsg_level = IPPROTO_IPV6; 208 | chdr->cmsg_type = IPV6_PKTINFO; 209 | chdr->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); 210 | struct in6_pktinfo *pktinfo = (struct in6_pktinfo*)CMSG_DATA(chdr); 211 | pktinfo->ipi6_ifindex = iface->ifindex; 212 | 213 | /* Also set scope ID if link-local */ 214 | if (IN6_IS_ADDR_LINKLOCAL(&dest->sin6_addr) 215 | || IN6_IS_ADDR_MC_LINKLOCAL(&dest->sin6_addr)) 216 | dest->sin6_scope_id = iface->ifindex; 217 | 218 | char ipbuf[INET6_ADDRSTRLEN]; 219 | inet_ntop(AF_INET6, &dest->sin6_addr, ipbuf, sizeof(ipbuf)); 220 | 221 | ssize_t sent = sendmsg(socket, &msg, MSG_DONTWAIT); 222 | if (sent < 0) 223 | syslog(LOG_ERR, "Failed to send to %s%%%s@%s (%m)", 224 | ipbuf, iface->name, iface->ifname); 225 | else 226 | syslog(LOG_DEBUG, "Sent %zd bytes to %s%%%s@%s", 227 | sent, ipbuf, iface->name, iface->ifname); 228 | return sent; 229 | } 230 | 231 | 232 | static int odhcpd_get_linklocal_interface_address(int ifindex, struct in6_addr *lladdr) 233 | { 234 | int ret = -1; 235 | struct sockaddr_in6 addr; 236 | socklen_t alen = sizeof(addr); 237 | int sock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6); 238 | 239 | if (sock < 0) 240 | return -1; 241 | 242 | memset(&addr, 0, sizeof(addr)); 243 | addr.sin6_family = AF_INET6; 244 | inet_pton(AF_INET6, ALL_IPV6_ROUTERS, &addr.sin6_addr); 245 | addr.sin6_scope_id = ifindex; 246 | 247 | if (!connect(sock, (struct sockaddr*)&addr, sizeof(addr)) && 248 | !getsockname(sock, (struct sockaddr*)&addr, &alen)) { 249 | *lladdr = addr.sin6_addr; 250 | ret = 0; 251 | } 252 | 253 | close(sock); 254 | return ret; 255 | } 256 | 257 | /* 258 | * DNS address selection criteria order : 259 | * - use IPv6 address with valid lifetime if none is yet selected 260 | * - use IPv6 address with a preferred lifetime if the already selected IPv6 address is deprecated 261 | * - use an IPv6 ULA address if the already selected IPv6 address is not an ULA address 262 | * - use the IPv6 address with the longest preferred lifetime 263 | */ 264 | int odhcpd_get_interface_dns_addr(const struct interface *iface, struct in6_addr *addr) 265 | { 266 | time_t now = odhcpd_time(); 267 | ssize_t m = -1; 268 | 269 | if (!iface->dns_service) 270 | return -1; 271 | 272 | for (size_t i = 0; i < iface->addr6_len; ++i) { 273 | if (iface->addr6[i].valid_lt <= (uint32_t)now) 274 | continue; 275 | 276 | if (m < 0) { 277 | m = i; 278 | continue; 279 | } 280 | 281 | if (iface->addr6[m].preferred_lt >= (uint32_t)now && 282 | iface->addr6[i].preferred_lt < (uint32_t)now) 283 | continue; 284 | 285 | if (IN6_IS_ADDR_ULA(&iface->addr6[i].addr.in6)) { 286 | if (!IN6_IS_ADDR_ULA(&iface->addr6[m].addr.in6)) { 287 | m = i; 288 | continue; 289 | } 290 | } else if (IN6_IS_ADDR_ULA(&iface->addr6[m].addr.in6)) 291 | continue; 292 | 293 | if (iface->addr6[i].preferred_lt > iface->addr6[m].preferred_lt) 294 | m = i; 295 | } 296 | 297 | if (m >= 0) { 298 | *addr = iface->addr6[m].addr.in6; 299 | return 0; 300 | } 301 | 302 | return odhcpd_get_linklocal_interface_address(iface->ifindex, addr); 303 | } 304 | 305 | struct interface* odhcpd_get_interface_by_index(int ifindex) 306 | { 307 | struct interface *iface; 308 | 309 | avl_for_each_element(&interfaces, iface, avl) { 310 | if (iface->ifindex == ifindex) 311 | return iface; 312 | } 313 | 314 | return NULL; 315 | } 316 | 317 | /* Convenience function to receive and do basic validation of packets */ 318 | static void odhcpd_receive_packets(struct uloop_fd *u, _unused unsigned int events) 319 | { 320 | struct odhcpd_event *e = container_of(u, struct odhcpd_event, uloop); 321 | 322 | uint8_t data_buf[8192], cmsg_buf[128]; 323 | union { 324 | struct sockaddr_in6 in6; 325 | struct sockaddr_in in; 326 | struct sockaddr_ll ll; 327 | struct sockaddr_nl nl; 328 | } addr; 329 | 330 | if (u->error) { 331 | int ret = -1; 332 | socklen_t ret_len = sizeof(ret); 333 | 334 | u->error = false; 335 | if (e->handle_error && getsockopt(u->fd, SOL_SOCKET, SO_ERROR, &ret, &ret_len) == 0) 336 | e->handle_error(e, ret); 337 | } 338 | 339 | if (e->recv_msgs) { 340 | e->recv_msgs(e); 341 | return; 342 | } 343 | 344 | while (true) { 345 | struct iovec iov = {data_buf, sizeof(data_buf)}; 346 | struct msghdr msg = { 347 | .msg_name = (void *) &addr, 348 | .msg_namelen = sizeof(addr), 349 | .msg_iov = &iov, 350 | .msg_iovlen = 1, 351 | .msg_control = cmsg_buf, 352 | .msg_controllen = sizeof(cmsg_buf), 353 | .msg_flags = 0 354 | }; 355 | 356 | ssize_t len = recvmsg(u->fd, &msg, MSG_DONTWAIT); 357 | if (len < 0) { 358 | if (errno == EAGAIN) 359 | break; 360 | else 361 | continue; 362 | } 363 | 364 | 365 | /* Extract destination interface */ 366 | int destiface = 0; 367 | int *hlim = NULL; 368 | void *dest = NULL; 369 | struct in6_pktinfo *pktinfo; 370 | struct in_pktinfo *pkt4info; 371 | for (struct cmsghdr *ch = CMSG_FIRSTHDR(&msg); ch != NULL; ch = CMSG_NXTHDR(&msg, ch)) { 372 | if (ch->cmsg_level == IPPROTO_IPV6 && 373 | ch->cmsg_type == IPV6_PKTINFO) { 374 | pktinfo = (struct in6_pktinfo*)CMSG_DATA(ch); 375 | destiface = pktinfo->ipi6_ifindex; 376 | dest = &pktinfo->ipi6_addr; 377 | } else if (ch->cmsg_level == IPPROTO_IP && 378 | ch->cmsg_type == IP_PKTINFO) { 379 | pkt4info = (struct in_pktinfo*)CMSG_DATA(ch); 380 | destiface = pkt4info->ipi_ifindex; 381 | dest = &pkt4info->ipi_addr; 382 | } else if (ch->cmsg_level == IPPROTO_IPV6 && 383 | ch->cmsg_type == IPV6_HOPLIMIT) { 384 | hlim = (int*)CMSG_DATA(ch); 385 | } 386 | } 387 | 388 | /* Check hoplimit if received */ 389 | if (hlim && *hlim != 255) 390 | continue; 391 | 392 | /* Detect interface for packet sockets */ 393 | if (addr.ll.sll_family == AF_PACKET) 394 | destiface = addr.ll.sll_ifindex; 395 | 396 | char ipbuf[INET6_ADDRSTRLEN] = "kernel"; 397 | if (addr.ll.sll_family == AF_PACKET && 398 | len >= (ssize_t)sizeof(struct ip6_hdr)) 399 | inet_ntop(AF_INET6, &data_buf[8], ipbuf, sizeof(ipbuf)); 400 | else if (addr.in6.sin6_family == AF_INET6) 401 | inet_ntop(AF_INET6, &addr.in6.sin6_addr, ipbuf, sizeof(ipbuf)); 402 | else if (addr.in.sin_family == AF_INET) 403 | inet_ntop(AF_INET, &addr.in.sin_addr, ipbuf, sizeof(ipbuf)); 404 | 405 | /* From netlink */ 406 | if (addr.nl.nl_family == AF_NETLINK) { 407 | syslog(LOG_DEBUG, "Received %zd Bytes from %s%%netlink", len, 408 | ipbuf); 409 | e->handle_dgram(&addr, data_buf, len, NULL, dest); 410 | return; 411 | } else if (destiface != 0) { 412 | struct interface *iface; 413 | 414 | avl_for_each_element(&interfaces, iface, avl) { 415 | if (iface->ifindex != destiface) 416 | continue; 417 | 418 | syslog(LOG_DEBUG, "Received %zd Bytes from %s%%%s@%s", len, 419 | ipbuf, iface->name, iface->ifname); 420 | 421 | e->handle_dgram(&addr, data_buf, len, iface, dest); 422 | } 423 | } 424 | 425 | 426 | } 427 | } 428 | 429 | /* Register events for the multiplexer */ 430 | int odhcpd_register(struct odhcpd_event *event) 431 | { 432 | event->uloop.cb = odhcpd_receive_packets; 433 | return uloop_fd_add(&event->uloop, ULOOP_READ | 434 | ((event->handle_error) ? ULOOP_ERROR_CB : 0)); 435 | } 436 | 437 | int odhcpd_deregister(struct odhcpd_event *event) 438 | { 439 | event->uloop.cb = NULL; 440 | return uloop_fd_delete(&event->uloop); 441 | } 442 | 443 | void odhcpd_process(struct odhcpd_event *event) 444 | { 445 | odhcpd_receive_packets(&event->uloop, 0); 446 | } 447 | 448 | int odhcpd_urandom(void *data, size_t len) 449 | { 450 | return read(urandom_fd, data, len); 451 | } 452 | 453 | 454 | time_t odhcpd_time(void) 455 | { 456 | struct timespec ts; 457 | clock_gettime(CLOCK_MONOTONIC, &ts); 458 | return ts.tv_sec; 459 | } 460 | 461 | 462 | static const char hexdigits[] = "0123456789abcdef"; 463 | static const int8_t hexvals[] = { 464 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -2, -2, -1, -1, -2, -1, -1, 465 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 466 | -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 467 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, 468 | -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, 469 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 470 | -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, 471 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 472 | }; 473 | 474 | ssize_t odhcpd_unhexlify(uint8_t *dst, size_t len, const char *src) 475 | { 476 | size_t c; 477 | for (c = 0; c < len && src[0] && src[1]; ++c) { 478 | int8_t x = (int8_t)*src++; 479 | int8_t y = (int8_t)*src++; 480 | if (x < 0 || (x = hexvals[x]) < 0 481 | || y < 0 || (y = hexvals[y]) < 0) 482 | return -1; 483 | dst[c] = x << 4 | y; 484 | while (((int8_t)*src) < 0 || 485 | (*src && hexvals[(uint8_t)*src] < 0)) 486 | src++; 487 | } 488 | 489 | return c; 490 | } 491 | 492 | 493 | void odhcpd_hexlify(char *dst, const uint8_t *src, size_t len) 494 | { 495 | for (size_t i = 0; i < len; ++i) { 496 | *dst++ = hexdigits[src[i] >> 4]; 497 | *dst++ = hexdigits[src[i] & 0x0f]; 498 | } 499 | *dst = 0; 500 | } 501 | 502 | const char *odhcpd_print_mac(const uint8_t *mac, const size_t len) 503 | { 504 | static char buf[32]; 505 | 506 | snprintf(buf, sizeof(buf), "%02x", mac[0]); 507 | for (size_t i = 1, j = 2; i < len && j < sizeof(buf); i++, j += 3) 508 | snprintf(buf + j, sizeof(buf) - j, ":%02x", mac[i]); 509 | 510 | return buf; 511 | } 512 | 513 | int odhcpd_bmemcmp(const void *av, const void *bv, size_t bits) 514 | { 515 | const uint8_t *a = av, *b = bv; 516 | size_t bytes = bits / 8; 517 | bits %= 8; 518 | 519 | int res = memcmp(a, b, bytes); 520 | if (res == 0 && bits > 0) 521 | res = (a[bytes] >> (8 - bits)) - (b[bytes] >> (8 - bits)); 522 | 523 | return res; 524 | } 525 | 526 | 527 | void odhcpd_bmemcpy(void *av, const void *bv, size_t bits) 528 | { 529 | uint8_t *a = av; 530 | const uint8_t *b = bv; 531 | 532 | size_t bytes = bits / 8; 533 | bits %= 8; 534 | memcpy(a, b, bytes); 535 | 536 | if (bits > 0) { 537 | uint8_t mask = (1 << (8 - bits)) - 1; 538 | a[bytes] = (a[bytes] & mask) | ((~mask) & b[bytes]); 539 | } 540 | } 541 | 542 | 543 | int odhcpd_parse_addr6_prefix(const char *str, struct in6_addr *addr, uint8_t *prefix) 544 | { 545 | size_t len; 546 | char *delim; 547 | 548 | *prefix = 0; 549 | if (!str) 550 | return -1; 551 | 552 | len = strlen(str); 553 | 554 | char buf[len + 1]; 555 | memcpy(buf, str, len); 556 | buf[len] = '\0'; 557 | 558 | delim = memchr(buf, '/', len); 559 | if (!delim) 560 | return -1; 561 | 562 | *(delim++) = '\0'; 563 | 564 | if (inet_pton(AF_INET6, buf, addr) != 1) 565 | return -1; 566 | 567 | if (sscanf(delim, "%" SCNu8, prefix) != 1 || *prefix > 128) { 568 | *prefix = 0; 569 | return -1; 570 | } 571 | 572 | return 0; 573 | } 574 | 575 | 576 | int odhcpd_netmask2bitlen(bool inet6, void *mask) 577 | { 578 | int bits; 579 | struct in_addr *v4; 580 | struct in6_addr *v6; 581 | 582 | if (inet6) 583 | for (bits = 0, v6 = mask; 584 | bits < 128 && (v6->s6_addr[bits / 8] << (bits % 8)) & 128; 585 | bits++); 586 | else 587 | for (bits = 0, v4 = mask; 588 | bits < 32 && (ntohl(v4->s_addr) << bits) & 0x80000000; 589 | bits++); 590 | 591 | return bits; 592 | } 593 | 594 | bool odhcpd_bitlen2netmask(bool inet6, unsigned int bits, void *mask) 595 | { 596 | uint8_t b; 597 | struct in_addr *v4; 598 | struct in6_addr *v6; 599 | 600 | if (inet6) 601 | { 602 | if (bits > 128) 603 | return false; 604 | 605 | v6 = mask; 606 | 607 | for (unsigned int i = 0; i < sizeof(v6->s6_addr); i++) 608 | { 609 | b = (bits > 8) ? 8 : bits; 610 | v6->s6_addr[i] = (uint8_t)(0xFF << (8 - b)); 611 | bits -= b; 612 | } 613 | } 614 | else 615 | { 616 | if (bits > 32) 617 | return false; 618 | 619 | v4 = mask; 620 | v4->s_addr = bits ? htonl(~((1 << (32 - bits)) - 1)) : 0; 621 | } 622 | 623 | return true; 624 | } 625 | 626 | bool odhcpd_valid_hostname(const char *name) 627 | { 628 | #define MAX_LABEL 63 629 | const char *c, *label, *label_end; 630 | int label_sz = 0; 631 | 632 | for (c = name, label_sz = 0, label = name, label_end = name + strcspn(name, ".") - 1; 633 | *c && label_sz <= MAX_LABEL; c++) { 634 | if ((*c >= '0' && *c <= '9') || 635 | (*c >= 'A' && *c <= 'Z') || 636 | (*c >= 'a' && *c <= 'z')) { 637 | label_sz++; 638 | continue; 639 | } 640 | 641 | if ((*c == '_' || *c == '-') && c != label && c != label_end) { 642 | label_sz++; 643 | continue; 644 | } 645 | 646 | if (*c == '.') { 647 | if (*(c + 1)) { 648 | label = c + 1; 649 | label_end = label + strcspn(label, ".") - 1; 650 | label_sz = 0; 651 | } 652 | continue; 653 | } 654 | 655 | return false; 656 | } 657 | 658 | return (label_sz && label_sz <= MAX_LABEL ? true : false); 659 | } 660 | -------------------------------------------------------------------------------- /src/odhcpd.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2012-2013 Steven Barth 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License v2 as published by 6 | * the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | * 13 | */ 14 | 15 | #pragma once 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #define min(a, b) (((a) < (b)) ? (a) : (b)) 30 | #define max(a, b) (((a) > (b)) ? (a) : (b)) 31 | 32 | // RFC 6106 defines this router advertisement option 33 | #define ND_OPT_ROUTE_INFO 24 34 | #define ND_OPT_RECURSIVE_DNS 25 35 | #define ND_OPT_DNS_SEARCH 31 36 | 37 | // RFC 8781 defines PREF64 option 38 | #define ND_OPT_PREF64 38 39 | 40 | // RFC 9463 - Discovery of Network-designated Resolvers (DNR) 41 | #define ND_OPT_DNR 144 42 | 43 | #define INFINITE_VALID(x) ((x) == 0) 44 | 45 | #define _unused __attribute__((unused)) 46 | #define _packed __attribute__((packed)) 47 | 48 | #define ALL_IPV6_NODES "ff02::1" 49 | #define ALL_IPV6_ROUTERS "ff02::2" 50 | 51 | #define NTP_SUBOPTION_SRV_ADDR 1 52 | #define NTP_SUBOPTION_MC_ADDR 2 53 | #define NTP_SUBOPTION_SRV_FQDN 3 54 | #define IPV6_ADDR_LEN 16 55 | 56 | #define IN6_IS_ADDR_ULA(a) (((a)->s6_addr32[0] & htonl(0xfe000000)) == htonl(0xfc000000)) 57 | 58 | #define ADDR_MATCH_PIO_FILTER(_addr, iface) (odhcpd_bmemcmp(&(_addr)->addr, \ 59 | &(iface)->pio_filter_addr, \ 60 | (iface)->pio_filter_length) != 0 || \ 61 | (_addr)->prefix < (iface)->pio_filter_length) 62 | 63 | struct interface; 64 | struct nl_sock; 65 | extern struct vlist_tree leases; 66 | extern struct config config; 67 | 68 | struct odhcpd_event { 69 | struct uloop_fd uloop; 70 | void (*handle_dgram)(void *addr, void *data, size_t len, 71 | struct interface *iface, void *dest_addr); 72 | void (*handle_error)(struct odhcpd_event *e, int error); 73 | void (*recv_msgs)(struct odhcpd_event *e); 74 | }; 75 | 76 | typedef int (*send_reply_cb_t)(const void *buf, size_t len, 77 | const struct sockaddr *dest, socklen_t dest_len, 78 | void *opaque); 79 | 80 | typedef void (*dhcpv6_binding_cb_handler_t)(struct in6_addr *addr, int prefix, 81 | uint32_t pref, uint32_t valid, 82 | void *arg); 83 | 84 | union if_addr { 85 | struct in_addr in; 86 | struct in6_addr in6; 87 | }; 88 | 89 | struct netevent_handler_info { 90 | struct interface *iface; 91 | union { 92 | struct { 93 | union if_addr dst; 94 | uint8_t dst_len; 95 | union if_addr gateway; 96 | } rt; 97 | struct { 98 | union if_addr dst; 99 | uint16_t state; 100 | uint8_t flags; 101 | } neigh; 102 | struct { 103 | struct odhcpd_ipaddr *addrs; 104 | size_t len; 105 | } addrs_old; 106 | union if_addr addr; 107 | }; 108 | }; 109 | 110 | enum netevents { 111 | NETEV_IFINDEX_CHANGE, 112 | NETEV_ADDR_ADD, 113 | NETEV_ADDR_DEL, 114 | NETEV_ADDRLIST_CHANGE, 115 | NETEV_ADDR6_ADD, 116 | NETEV_ADDR6_DEL, 117 | NETEV_ADDR6LIST_CHANGE, 118 | NETEV_ROUTE6_ADD, 119 | NETEV_ROUTE6_DEL, 120 | NETEV_NEIGH6_ADD, 121 | NETEV_NEIGH6_DEL, 122 | }; 123 | 124 | struct netevent_handler { 125 | struct list_head head; 126 | void (*cb) (unsigned long event, struct netevent_handler_info *info); 127 | }; 128 | 129 | struct odhcpd_ipaddr { 130 | union if_addr addr; 131 | uint8_t prefix; 132 | uint32_t preferred_lt; 133 | uint32_t valid_lt; 134 | 135 | union { 136 | /* ipv6 only */ 137 | struct { 138 | uint8_t dprefix; 139 | uint8_t invalid_advertisements; 140 | bool tentative; 141 | }; 142 | 143 | /* ipv4 only */ 144 | struct in_addr broadcast; 145 | }; 146 | }; 147 | 148 | enum odhcpd_mode { 149 | MODE_DISABLED, 150 | MODE_SERVER, 151 | MODE_RELAY, 152 | MODE_HYBRID 153 | }; 154 | 155 | 156 | enum odhcpd_assignment_flags { 157 | OAF_TENTATIVE = (1 << 0), 158 | OAF_BOUND = (1 << 1), 159 | OAF_STATIC = (1 << 2), 160 | OAF_BROKEN_HOSTNAME = (1 << 3), 161 | OAF_DHCPV4 = (1 << 4), 162 | OAF_DHCPV6_NA = (1 << 5), 163 | OAF_DHCPV6_PD = (1 << 6), 164 | }; 165 | 166 | struct config { 167 | bool legacy; 168 | bool main_dhcpv4; 169 | char *dhcp_cb; 170 | char *dhcp_statefile; 171 | char *dhcp_hostsfile; 172 | int log_level; 173 | }; 174 | 175 | 176 | struct lease { 177 | struct vlist_node node; 178 | struct list_head assignments; 179 | uint32_t ipaddr; 180 | uint64_t hostid; 181 | struct ether_addr mac; 182 | uint16_t duid_len; 183 | uint8_t *duid; 184 | uint32_t leasetime; 185 | char *hostname; 186 | }; 187 | 188 | enum { 189 | LEASE_ATTR_IP, 190 | LEASE_ATTR_MAC, 191 | LEASE_ATTR_DUID, 192 | LEASE_ATTR_HOSTID, 193 | LEASE_ATTR_LEASETIME, 194 | LEASE_ATTR_NAME, 195 | LEASE_ATTR_MAX 196 | }; 197 | 198 | struct odhcpd_ref_ip; 199 | 200 | struct dhcp_assignment { 201 | struct list_head head; 202 | struct list_head lease_list; 203 | 204 | void (*dhcp_free_cb)(struct dhcp_assignment *a); 205 | 206 | struct interface *iface; 207 | struct lease *lease; 208 | 209 | struct sockaddr_in6 peer; 210 | time_t valid_until; 211 | time_t preferred_until; 212 | 213 | #define fr_timer reconf_timer 214 | struct uloop_timeout reconf_timer; 215 | #define accept_fr_nonce accept_reconf 216 | bool accept_reconf; 217 | #define fr_cnt reconf_cnt 218 | int reconf_cnt; 219 | uint8_t key[16]; 220 | struct odhcpd_ref_ip *fr_ip; 221 | 222 | uint32_t addr; 223 | union { 224 | uint64_t assigned_host_id; 225 | uint32_t assigned_subnet_id; 226 | }; 227 | uint32_t iaid; 228 | uint8_t length; // length == 128 -> IA_NA, length <= 64 -> IA_PD 229 | 230 | struct odhcpd_ipaddr *managed; 231 | ssize_t managed_size; 232 | struct ustream_fd managed_sock; 233 | 234 | unsigned int flags; 235 | uint32_t leasetime; 236 | char *hostname; 237 | uint8_t *reqopts; 238 | size_t reqopts_len; 239 | 240 | #define hwaddr mac 241 | uint8_t mac[6]; 242 | 243 | uint16_t clid_len; 244 | uint8_t clid_data[]; 245 | }; 246 | 247 | 248 | // DNR - RFC9463 249 | struct dnr_options { 250 | uint16_t priority; 251 | 252 | uint32_t lifetime; 253 | bool lifetime_set; 254 | 255 | uint8_t *adn; 256 | uint16_t adn_len; 257 | 258 | struct in_addr *addr4; 259 | size_t addr4_cnt; 260 | struct in6_addr *addr6; 261 | size_t addr6_cnt; 262 | 263 | uint8_t *svc; 264 | uint16_t svc_len; 265 | }; 266 | 267 | 268 | struct interface { 269 | struct avl_node avl; 270 | 271 | int ifflags; 272 | int ifindex; 273 | char *ifname; 274 | const char *name; 275 | 276 | // IPv6 runtime data 277 | struct odhcpd_ipaddr *addr6; 278 | size_t addr6_len; 279 | struct odhcpd_ipaddr *invalid_addr6; 280 | size_t invalid_addr6_len; 281 | 282 | // RA runtime data 283 | struct odhcpd_event router_event; 284 | struct uloop_timeout timer_rs; 285 | uint32_t ra_sent; 286 | 287 | // DHCPv6 runtime data 288 | struct odhcpd_event dhcpv6_event; 289 | struct list_head ia_assignments; 290 | 291 | // NDP runtime data 292 | struct odhcpd_event ndp_event; 293 | int ndp_ping_fd; 294 | 295 | // IPv4 runtime data 296 | struct odhcpd_ipaddr *addr4; 297 | size_t addr4_len; 298 | 299 | // DHCPv4 runtime data 300 | struct odhcpd_event dhcpv4_event; 301 | struct list_head dhcpv4_assignments; 302 | struct list_head dhcpv4_fr_ips; 303 | 304 | // Managed PD 305 | char dhcpv6_pd_manager[128]; 306 | struct in6_addr dhcpv6_pd_cer; 307 | 308 | // Services 309 | enum odhcpd_mode ra; 310 | enum odhcpd_mode dhcpv6; 311 | enum odhcpd_mode ndp; 312 | enum odhcpd_mode dhcpv4; 313 | 314 | // Config 315 | bool inuse; 316 | bool external; 317 | bool master; 318 | bool ignore; 319 | bool always_rewrite_dns; 320 | bool dns_service; 321 | 322 | // NDP 323 | int learn_routes; 324 | 325 | // RA 326 | uint8_t ra_flags; 327 | bool ra_slaac; 328 | bool ra_not_onlink; 329 | bool ra_advrouter; 330 | bool ra_useleasetime; 331 | bool ra_dns; 332 | uint8_t pref64_length; 333 | uint8_t pref64_plc; 334 | uint32_t pref64_prefix[3]; 335 | bool no_dynamic_dhcp; 336 | bool have_link_local; 337 | uint8_t pio_filter_length; 338 | struct in6_addr pio_filter_addr; 339 | int default_router; 340 | int route_preference; 341 | int ra_maxinterval; 342 | int ra_mininterval; 343 | int ra_lifetime; 344 | uint32_t ra_reachabletime; 345 | uint32_t ra_retranstime; 346 | uint32_t ra_hoplimit; 347 | int ra_mtu; 348 | uint32_t preferred_lifetime; 349 | 350 | // DHCP 351 | uint32_t dhcp_leasetime; 352 | 353 | // DHCPv4 354 | struct in_addr dhcpv4_start; 355 | struct in_addr dhcpv4_end; 356 | struct in_addr dhcpv4_start_ip; 357 | struct in_addr dhcpv4_end_ip; 358 | struct in_addr dhcpv4_local; 359 | struct in_addr dhcpv4_bcast; 360 | struct in_addr dhcpv4_mask; 361 | struct in_addr *dhcpv4_router; 362 | size_t dhcpv4_router_cnt; 363 | struct in_addr *dhcpv4_dns; 364 | size_t dhcpv4_dns_cnt; 365 | bool dhcpv4_forcereconf; 366 | 367 | // DNS 368 | struct in6_addr *dns; 369 | size_t dns_cnt; 370 | uint8_t *search; 371 | size_t search_len; 372 | 373 | // DHCPV6 374 | void *dhcpv6_raw; 375 | size_t dhcpv6_raw_len; 376 | bool dhcpv6_assignall; 377 | bool dhcpv6_pd; 378 | bool dhcpv6_na; 379 | uint32_t dhcpv6_hostid_len; 380 | uint32_t dhcpv6_pd_min_len; // minimum delegated prefix length 381 | 382 | char *upstream; 383 | size_t upstream_len; 384 | 385 | char *filter_class; 386 | 387 | // NTP 388 | struct in_addr *dhcpv4_ntp; 389 | size_t dhcpv4_ntp_cnt; 390 | uint8_t *dhcpv6_ntp; 391 | uint16_t dhcpv6_ntp_len; 392 | size_t dhcpv6_ntp_cnt; 393 | 394 | // SNTP 395 | struct in6_addr *dhcpv6_sntp; 396 | size_t dhcpv6_sntp_cnt; 397 | 398 | // DNR 399 | struct dnr_options *dnr; 400 | size_t dnr_cnt; 401 | }; 402 | 403 | extern struct avl_tree interfaces; 404 | extern const struct blobmsg_policy lease_attrs[LEASE_ATTR_MAX]; 405 | 406 | inline static void free_assignment(struct dhcp_assignment *a) 407 | { 408 | list_del(&a->head); 409 | list_del(&a->lease_list); 410 | 411 | if (a->dhcp_free_cb) 412 | a->dhcp_free_cb(a); 413 | 414 | free(a->hostname); 415 | free(a->reqopts); 416 | free(a); 417 | } 418 | 419 | inline static struct dhcp_assignment *alloc_assignment(size_t extra_len) 420 | { 421 | struct dhcp_assignment *a = calloc(1, sizeof(*a) + extra_len); 422 | 423 | if (!a) 424 | return NULL; 425 | 426 | INIT_LIST_HEAD(&a->head); 427 | INIT_LIST_HEAD(&a->lease_list); 428 | 429 | return a; 430 | } 431 | 432 | // Exported main functions 433 | int odhcpd_register(struct odhcpd_event *event); 434 | int odhcpd_deregister(struct odhcpd_event *event); 435 | void odhcpd_process(struct odhcpd_event *event); 436 | 437 | ssize_t odhcpd_send(int socket, struct sockaddr_in6 *dest, 438 | struct iovec *iov, size_t iov_len, 439 | const struct interface *iface); 440 | int odhcpd_get_interface_dns_addr(const struct interface *iface, 441 | struct in6_addr *addr); 442 | int odhcpd_get_interface_config(const char *ifname, const char *what); 443 | int odhcpd_get_mac(const struct interface *iface, uint8_t mac[6]); 444 | int odhcpd_get_flags(const struct interface *iface); 445 | struct interface* odhcpd_get_interface_by_index(int ifindex); 446 | int odhcpd_urandom(void *data, size_t len); 447 | 448 | void odhcpd_run(void); 449 | time_t odhcpd_time(void); 450 | ssize_t odhcpd_unhexlify(uint8_t *dst, size_t len, const char *src); 451 | void odhcpd_hexlify(char *dst, const uint8_t *src, size_t len); 452 | const char *odhcpd_print_mac(const uint8_t *mac, const size_t len); 453 | 454 | int odhcpd_bmemcmp(const void *av, const void *bv, size_t bits); 455 | void odhcpd_bmemcpy(void *av, const void *bv, size_t bits); 456 | 457 | int odhcpd_parse_addr6_prefix(const char *str, struct in6_addr *addr, uint8_t *prefix); 458 | int odhcpd_netmask2bitlen(bool v6, void *mask); 459 | bool odhcpd_bitlen2netmask(bool v6, unsigned int bits, void *mask); 460 | bool odhcpd_valid_hostname(const char *name); 461 | 462 | int config_parse_interface(void *data, size_t len, const char *iname, bool overwrite); 463 | struct lease *config_find_lease_by_duid(const uint8_t *duid, const uint16_t len); 464 | struct lease *config_find_lease_by_mac(const uint8_t *mac); 465 | struct lease *config_find_lease_by_hostid(const uint64_t hostid); 466 | struct lease *config_find_lease_by_ipaddr(const uint32_t ipaddr); 467 | int set_lease_from_blobmsg(struct blob_attr *ba); 468 | 469 | #ifdef WITH_UBUS 470 | int ubus_init(void); 471 | const char* ubus_get_ifname(const char *name); 472 | void ubus_apply_network(void); 473 | bool ubus_has_prefix(const char *name, const char *ifname); 474 | void ubus_bcast_dhcp_event(const char *type, const uint8_t *mac, const size_t mac_len, 475 | const struct in_addr *addr, const char *name, const char *interface); 476 | #endif 477 | 478 | ssize_t dhcpv6_ia_handle_IAs(uint8_t *buf, size_t buflen, struct interface *iface, 479 | const struct sockaddr_in6 *addr, const void *data, const uint8_t *end); 480 | int dhcpv6_ia_init(void); 481 | int dhcpv6_ia_setup_interface(struct interface *iface, bool enable); 482 | void dhcpv6_ia_enum_addrs(struct interface *iface, struct dhcp_assignment *c, time_t now, 483 | dhcpv6_binding_cb_handler_t func, void *arg); 484 | void dhcpv6_ia_write_statefile(void); 485 | 486 | int netlink_add_netevent_handler(struct netevent_handler *hdlr); 487 | ssize_t netlink_get_interface_addrs(const int ifindex, bool v6, 488 | struct odhcpd_ipaddr **addrs); 489 | ssize_t netlink_get_interface_linklocal(int ifindex, struct odhcpd_ipaddr **addrs); 490 | int netlink_get_interface_proxy_neigh(int ifindex, const struct in6_addr *addr); 491 | int netlink_setup_route(const struct in6_addr *addr, const int prefixlen, 492 | const int ifindex, const struct in6_addr *gw, 493 | const uint32_t metric, const bool add); 494 | int netlink_setup_proxy_neigh(const struct in6_addr *addr, 495 | const int ifindex, const bool add); 496 | int netlink_setup_addr(struct odhcpd_ipaddr *addr, 497 | const int ifindex, const bool v6, const bool add); 498 | void netlink_dump_neigh_table(const bool proxy); 499 | void netlink_dump_addr_table(const bool v6); 500 | 501 | // Exported module initializers 502 | int netlink_init(void); 503 | int router_init(void); 504 | int dhcpv6_init(void); 505 | int ndp_init(void); 506 | #ifdef DHCPV4_SUPPORT 507 | int dhcpv4_init(void); 508 | 509 | int dhcpv4_setup_interface(struct interface *iface, bool enable); 510 | void dhcpv4_handle_msg(void *addr, void *data, size_t len, 511 | struct interface *iface, _unused void *dest_addr, 512 | send_reply_cb_t send_reply, void *opaque); 513 | #endif 514 | int router_setup_interface(struct interface *iface, bool enable); 515 | int dhcpv6_setup_interface(struct interface *iface, bool enable); 516 | int ndp_setup_interface(struct interface *iface, bool enable); 517 | void reload_services(struct interface *iface); 518 | 519 | void odhcpd_reload(void); 520 | -------------------------------------------------------------------------------- /src/router.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2012-2013 Steven Barth 3 | * Copyright (C) 2018 Hans Dedecker 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License v2 as published by 7 | * the Free Software Foundation. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | */ 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include 28 | 29 | #include "router.h" 30 | #include "odhcpd.h" 31 | 32 | 33 | static void forward_router_solicitation(const struct interface *iface); 34 | static void forward_router_advertisement(const struct interface *iface, uint8_t *data, size_t len); 35 | 36 | static void handle_icmpv6(void *addr, void *data, size_t len, 37 | struct interface *iface, void *dest); 38 | static void trigger_router_advert(struct uloop_timeout *event); 39 | static void router_netevent_cb(unsigned long event, struct netevent_handler_info *info); 40 | 41 | static struct netevent_handler router_netevent_handler = { .cb = router_netevent_cb, }; 42 | 43 | static FILE *fp_route = NULL; 44 | 45 | 46 | #define TIME_LEFT(t1, now) ((t1) != UINT32_MAX ? (t1) - (now) : UINT32_MAX) 47 | 48 | int router_init(void) 49 | { 50 | int ret = 0; 51 | 52 | if (!(fp_route = fopen("/proc/net/ipv6_route", "r"))) { 53 | syslog(LOG_ERR, "fopen(/proc/net/ipv6_route): %m"); 54 | ret = -1; 55 | goto out; 56 | } 57 | 58 | if (netlink_add_netevent_handler(&router_netevent_handler) < 0) { 59 | syslog(LOG_ERR, "Failed to add netevent handler"); 60 | ret = -1; 61 | } 62 | 63 | out: 64 | if (ret < 0 && fp_route) { 65 | fclose(fp_route); 66 | fp_route = NULL; 67 | } 68 | 69 | return ret; 70 | } 71 | 72 | 73 | int router_setup_interface(struct interface *iface, bool enable) 74 | { 75 | int ret = 0; 76 | 77 | enable = enable && (iface->ra != MODE_DISABLED); 78 | 79 | if (!fp_route) { 80 | ret = -1; 81 | goto out; 82 | } 83 | 84 | 85 | if (!enable && iface->router_event.uloop.fd >= 0) { 86 | if (!iface->master) { 87 | uloop_timeout_cancel(&iface->timer_rs); 88 | iface->timer_rs.cb = NULL; 89 | 90 | trigger_router_advert(&iface->timer_rs); 91 | } 92 | 93 | uloop_fd_delete(&iface->router_event.uloop); 94 | close(iface->router_event.uloop.fd); 95 | iface->router_event.uloop.fd = -1; 96 | } else if (enable) { 97 | struct icmp6_filter filt; 98 | struct ipv6_mreq mreq; 99 | int val = 2; 100 | 101 | if (iface->router_event.uloop.fd < 0) { 102 | /* Open ICMPv6 socket */ 103 | iface->router_event.uloop.fd = socket(AF_INET6, SOCK_RAW | SOCK_CLOEXEC, 104 | IPPROTO_ICMPV6); 105 | if (iface->router_event.uloop.fd < 0) { 106 | syslog(LOG_ERR, "socket(AF_INET6): %m"); 107 | ret = -1; 108 | goto out; 109 | } 110 | 111 | if (setsockopt(iface->router_event.uloop.fd, SOL_SOCKET, SO_BINDTODEVICE, 112 | iface->ifname, strlen(iface->ifname)) < 0) { 113 | syslog(LOG_ERR, "setsockopt(SO_BINDTODEVICE): %m"); 114 | ret = -1; 115 | goto out; 116 | } 117 | 118 | /* Let the kernel compute our checksums */ 119 | if (setsockopt(iface->router_event.uloop.fd, IPPROTO_RAW, IPV6_CHECKSUM, 120 | &val, sizeof(val)) < 0) { 121 | syslog(LOG_ERR, "setsockopt(IPV6_CHECKSUM): %m"); 122 | ret = -1; 123 | goto out; 124 | } 125 | 126 | /* This is required by RFC 4861 */ 127 | val = 255; 128 | if (setsockopt(iface->router_event.uloop.fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, 129 | &val, sizeof(val)) < 0) { 130 | syslog(LOG_ERR, "setsockopt(IPV6_MULTICAST_HOPS): %m"); 131 | ret = -1; 132 | goto out; 133 | } 134 | 135 | if (setsockopt(iface->router_event.uloop.fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, 136 | &val, sizeof(val)) < 0) { 137 | syslog(LOG_ERR, "setsockopt(IPV6_UNICAST_HOPS): %m"); 138 | ret = -1; 139 | goto out; 140 | } 141 | 142 | /* We need to know the source interface */ 143 | val = 1; 144 | if (setsockopt(iface->router_event.uloop.fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, 145 | &val, sizeof(val)) < 0) { 146 | syslog(LOG_ERR, "setsockopt(IPV6_RECVPKTINFO): %m"); 147 | ret = -1; 148 | goto out; 149 | } 150 | 151 | if (setsockopt(iface->router_event.uloop.fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, 152 | &val, sizeof(val)) < 0) { 153 | syslog(LOG_ERR, "setsockopt(IPV6_RECVHOPLIMIT): %m"); 154 | ret = -1; 155 | goto out; 156 | } 157 | 158 | /* Don't loop back */ 159 | val = 0; 160 | if (setsockopt(iface->router_event.uloop.fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, 161 | &val, sizeof(val)) < 0) { 162 | syslog(LOG_ERR, "setsockopt(IPV6_MULTICAST_LOOP): %m"); 163 | ret = -1; 164 | goto out; 165 | } 166 | 167 | /* Filter ICMPv6 package types */ 168 | ICMP6_FILTER_SETBLOCKALL(&filt); 169 | ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filt); 170 | ICMP6_FILTER_SETPASS(ND_ROUTER_SOLICIT, &filt); 171 | if (setsockopt(iface->router_event.uloop.fd, IPPROTO_ICMPV6, ICMP6_FILTER, 172 | &filt, sizeof(filt)) < 0) { 173 | syslog(LOG_ERR, "setsockopt(ICMP6_FILTER): %m"); 174 | ret = -1; 175 | goto out; 176 | } 177 | 178 | iface->router_event.handle_dgram = handle_icmpv6; 179 | iface->ra_sent = 0; 180 | odhcpd_register(&iface->router_event); 181 | } else { 182 | uloop_timeout_cancel(&iface->timer_rs); 183 | iface->timer_rs.cb = NULL; 184 | 185 | memset(&mreq, 0, sizeof(mreq)); 186 | mreq.ipv6mr_interface = iface->ifindex; 187 | inet_pton(AF_INET6, ALL_IPV6_NODES, &mreq.ipv6mr_multiaddr); 188 | setsockopt(iface->router_event.uloop.fd, IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP, 189 | &mreq, sizeof(mreq)); 190 | 191 | inet_pton(AF_INET6, ALL_IPV6_ROUTERS, &mreq.ipv6mr_multiaddr); 192 | setsockopt(iface->router_event.uloop.fd, IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP, 193 | &mreq, sizeof(mreq)); 194 | } 195 | 196 | memset(&mreq, 0, sizeof(mreq)); 197 | mreq.ipv6mr_interface = iface->ifindex; 198 | inet_pton(AF_INET6, ALL_IPV6_ROUTERS, &mreq.ipv6mr_multiaddr); 199 | 200 | if (iface->ra == MODE_RELAY && iface->master) { 201 | inet_pton(AF_INET6, ALL_IPV6_NODES, &mreq.ipv6mr_multiaddr); 202 | forward_router_solicitation(iface); 203 | } else if (iface->ra == MODE_SERVER) { 204 | iface->timer_rs.cb = trigger_router_advert; 205 | uloop_timeout_set(&iface->timer_rs, 1000); 206 | } 207 | 208 | if (setsockopt(iface->router_event.uloop.fd, IPPROTO_IPV6, 209 | IPV6_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) { 210 | ret = -1; 211 | syslog(LOG_ERR, "setsockopt(IPV6_ADD_MEMBERSHIP): %m"); 212 | goto out; 213 | } 214 | } 215 | out: 216 | if (ret < 0 && iface->router_event.uloop.fd >= 0) { 217 | if (iface->router_event.uloop.registered) 218 | uloop_fd_delete(&iface->router_event.uloop); 219 | 220 | close(iface->router_event.uloop.fd); 221 | iface->router_event.uloop.fd = -1; 222 | } 223 | 224 | return ret; 225 | } 226 | 227 | 228 | static void router_netevent_cb(unsigned long event, struct netevent_handler_info *info) 229 | { 230 | struct interface *iface; 231 | 232 | switch (event) { 233 | case NETEV_IFINDEX_CHANGE: 234 | iface = info->iface; 235 | if (iface && iface->router_event.uloop.fd >= 0) { 236 | if (iface->router_event.uloop.registered) 237 | uloop_fd_delete(&iface->router_event.uloop); 238 | 239 | close(iface->router_event.uloop.fd); 240 | iface->router_event.uloop.fd = -1; 241 | } 242 | break; 243 | case NETEV_ROUTE6_ADD: 244 | case NETEV_ROUTE6_DEL: 245 | if (info->rt.dst_len) 246 | break; 247 | 248 | avl_for_each_element(&interfaces, iface, avl) { 249 | if (iface->ra == MODE_SERVER && !iface->master) 250 | uloop_timeout_set(&iface->timer_rs, 1000); 251 | } 252 | break; 253 | case NETEV_ADDR6LIST_CHANGE: 254 | iface = info->iface; 255 | if (iface && iface->ra == MODE_SERVER && !iface->master) 256 | uloop_timeout_set(&iface->timer_rs, 1000); 257 | break; 258 | default: 259 | break; 260 | } 261 | } 262 | 263 | 264 | static bool router_icmpv6_valid(struct sockaddr_in6 *source, uint8_t *data, size_t len) 265 | { 266 | struct icmp6_hdr *hdr = (struct icmp6_hdr *)data; 267 | struct icmpv6_opt *opt, *end = (struct icmpv6_opt*)&data[len]; 268 | 269 | /* Hoplimit is already checked in odhcpd_receive_packets */ 270 | if (len < sizeof(*hdr) || hdr->icmp6_code) 271 | return false; 272 | 273 | switch (hdr->icmp6_type) { 274 | case ND_ROUTER_ADVERT: 275 | if (!IN6_IS_ADDR_LINKLOCAL(&source->sin6_addr)) 276 | return false; 277 | 278 | opt = (struct icmpv6_opt *)((struct nd_router_advert *)data + 1); 279 | break; 280 | 281 | case ND_ROUTER_SOLICIT: 282 | opt = (struct icmpv6_opt *)((struct nd_router_solicit *)data + 1); 283 | break; 284 | 285 | default: 286 | return false; 287 | } 288 | 289 | icmpv6_for_each_option(opt, opt, end) 290 | if (opt->type == ND_OPT_SOURCE_LINKADDR && 291 | IN6_IS_ADDR_UNSPECIFIED(&source->sin6_addr) && 292 | hdr->icmp6_type == ND_ROUTER_SOLICIT) 293 | return false; 294 | 295 | /* Check all options parsed successfully */ 296 | return opt == end; 297 | } 298 | 299 | 300 | /* Detect whether a default route exists, also find the source prefixes */ 301 | static bool parse_routes(struct odhcpd_ipaddr *n, ssize_t len) 302 | { 303 | struct odhcpd_ipaddr p = { .addr.in6 = IN6ADDR_ANY_INIT, .prefix = 0, 304 | .dprefix = 0, .preferred_lt = 0, .valid_lt = 0}; 305 | bool found_default = false; 306 | char line[512], ifname[16]; 307 | 308 | rewind(fp_route); 309 | 310 | while (fgets(line, sizeof(line), fp_route)) { 311 | uint32_t rflags; 312 | if (sscanf(line, "00000000000000000000000000000000 00 " 313 | "%*s %*s %*s %*s %*s %*s %*s %15s", ifname) && 314 | strcmp(ifname, "lo")) { 315 | found_default = true; 316 | } else if (sscanf(line, "%8" SCNx32 "%8" SCNx32 "%*8" SCNx32 "%*8" SCNx32 " %hhx %*s " 317 | "%*s 00000000000000000000000000000000 %*s %*s %*s %" SCNx32 " lo", 318 | &p.addr.in6.s6_addr32[0], &p.addr.in6.s6_addr32[1], &p.prefix, &rflags) && 319 | p.prefix > 0 && (rflags & RTF_NONEXTHOP) && (rflags & RTF_REJECT)) { 320 | // Find source prefixes by scanning through unreachable-routes 321 | p.addr.in6.s6_addr32[0] = htonl(p.addr.in6.s6_addr32[0]); 322 | p.addr.in6.s6_addr32[1] = htonl(p.addr.in6.s6_addr32[1]); 323 | 324 | for (ssize_t i = 0; i < len; ++i) { 325 | if (n[i].prefix <= 64 && n[i].prefix >= p.prefix && 326 | !odhcpd_bmemcmp(&p.addr.in6, &n[i].addr.in6, p.prefix)) { 327 | n[i].dprefix = p.prefix; 328 | break; 329 | } 330 | } 331 | } 332 | } 333 | 334 | return found_default; 335 | } 336 | 337 | static int calc_adv_interval(struct interface *iface, uint32_t lowest_found_lifetime, 338 | uint32_t *maxival) 339 | { 340 | uint32_t minival = iface->ra_mininterval; 341 | int msecs; 342 | 343 | *maxival = iface->ra_maxinterval; 344 | 345 | /* 346 | * rfc4861#section-6.2.1 : AdvDefaultLifetime Default: 3 * MaxRtrAdvInterval 347 | * therefore max interval shall be no greater than 1/3 of the lowest valid 348 | * lease time of all known prefixes. 349 | */ 350 | if (*maxival > lowest_found_lifetime/3) 351 | *maxival = lowest_found_lifetime/3; 352 | 353 | if (*maxival > MaxRtrAdvInterval) 354 | *maxival = MaxRtrAdvInterval; 355 | else if (*maxival < 4) 356 | *maxival = 4; 357 | 358 | if (minival < MinRtrAdvInterval) 359 | minival = MinRtrAdvInterval; 360 | else if (minival > (*maxival * 3)/4) 361 | minival = (*maxival >= 9 ? *maxival/3 : *maxival); 362 | 363 | odhcpd_urandom(&msecs, sizeof(msecs)); 364 | msecs = (labs(msecs) % ((*maxival != minival) ? (*maxival - minival)*1000 : 500)) + 365 | minival*1000; 366 | 367 | /* RFC 2461 6.2.4 For the first MAX_INITIAL_RTR_ADVERTISEMENTS advertisements */ 368 | /* if the timer is bigger than MAX_INITIAL_RTR_ADVERT_INTERVAL it should be */ 369 | /* set to MAX_INITIAL_RTR_ADVERT_INTERVAL */ 370 | /* Off by one as an initial interval timer has already expired */ 371 | if ((iface->ra_sent + 1) < MaxInitialRtAdvs && msecs > MaxInitialRtrAdvInterval*1000) 372 | msecs = MaxInitialRtrAdvInterval*1000; 373 | 374 | return msecs; 375 | } 376 | 377 | static uint32_t calc_ra_lifetime(struct interface *iface, uint32_t maxival) 378 | { 379 | uint32_t lifetime = 3*maxival; 380 | 381 | if (iface->ra_lifetime >= 0) { 382 | lifetime = iface->ra_lifetime; 383 | if (lifetime > 0 && lifetime < maxival) 384 | lifetime = maxival; 385 | else if (lifetime > 9000) 386 | lifetime = 9000; 387 | } 388 | 389 | return lifetime; 390 | } 391 | 392 | enum { 393 | IOV_RA_ADV=0, 394 | IOV_RA_PFXS, 395 | IOV_RA_ROUTES, 396 | IOV_RA_DNS, 397 | IOV_RA_SEARCH, 398 | IOV_RA_PREF64, 399 | IOV_RA_DNR, 400 | IOV_RA_ADV_INTERVAL, 401 | IOV_RA_TOTAL, 402 | }; 403 | 404 | struct adv_msg { 405 | struct nd_router_advert h; 406 | struct icmpv6_opt lladdr; 407 | struct nd_opt_mtu mtu; 408 | }; 409 | 410 | struct nd_opt_dns_server { 411 | uint8_t type; 412 | uint8_t len; 413 | uint8_t pad; 414 | uint8_t pad2; 415 | uint32_t lifetime; 416 | struct in6_addr addr[]; 417 | }; 418 | 419 | struct nd_opt_search_list { 420 | uint8_t type; 421 | uint8_t len; 422 | uint8_t pad; 423 | uint8_t pad2; 424 | uint32_t lifetime; 425 | uint8_t name[]; 426 | }; 427 | 428 | struct nd_opt_route_info { 429 | uint8_t type; 430 | uint8_t len; 431 | uint8_t prefix; 432 | uint8_t flags; 433 | uint32_t lifetime; 434 | uint32_t addr[4]; 435 | }; 436 | 437 | struct nd_opt_pref64_info { 438 | uint8_t type; 439 | uint8_t len; 440 | uint16_t lifetime_plc; 441 | uint32_t prefix[3]; 442 | }; 443 | 444 | struct nd_opt_dnr_info { 445 | uint8_t type; 446 | uint8_t len; 447 | uint16_t priority; 448 | uint32_t lifetime; 449 | uint16_t adn_len; 450 | uint8_t body[]; 451 | }; 452 | 453 | /* Router Advert server mode */ 454 | static int send_router_advert(struct interface *iface, const struct in6_addr *from) 455 | { 456 | time_t now = odhcpd_time(); 457 | struct odhcpd_ipaddr *addrs = NULL; 458 | struct adv_msg adv; 459 | struct nd_opt_prefix_info *pfxs = NULL; 460 | struct nd_opt_dns_server *dns = NULL; 461 | struct nd_opt_search_list *search = NULL; 462 | struct nd_opt_route_info *routes = NULL; 463 | struct nd_opt_pref64_info *pref64 = NULL; 464 | struct nd_opt_dnr_info *dnrs = NULL; 465 | struct nd_opt_adv_interval adv_interval; 466 | struct iovec iov[IOV_RA_TOTAL]; 467 | struct sockaddr_in6 dest; 468 | size_t dns_sz = 0, search_sz = 0, pref64_sz = 0, dnrs_sz = 0; 469 | size_t pfxs_cnt = 0, routes_cnt = 0; 470 | ssize_t valid_addr_cnt = 0, invalid_addr_cnt = 0; 471 | /* 472 | * lowest_found_lifetime stores the lowest lifetime of all prefixes; 473 | * necessary to find shortest adv interval necessary 474 | * for shortest lived prefix 475 | */ 476 | uint32_t lowest_found_lifetime = UINT32_MAX, maxival, lifetime; 477 | int msecs, mtu = iface->ra_mtu, hlim = iface->ra_hoplimit; 478 | bool default_route = false; 479 | bool valid_prefix = false; 480 | char buf[INET6_ADDRSTRLEN]; 481 | 482 | memset(&adv, 0, sizeof(adv)); 483 | adv.h.nd_ra_type = ND_ROUTER_ADVERT; 484 | 485 | if (hlim == 0) 486 | hlim = odhcpd_get_interface_config(iface->ifname, "hop_limit"); 487 | 488 | if (hlim > 0) 489 | adv.h.nd_ra_curhoplimit = hlim; 490 | 491 | adv.h.nd_ra_flags_reserved = iface->ra_flags; 492 | 493 | if (iface->route_preference < 0) 494 | adv.h.nd_ra_flags_reserved |= ND_RA_PREF_LOW; 495 | else if (iface->route_preference > 0) 496 | adv.h.nd_ra_flags_reserved |= ND_RA_PREF_HIGH; 497 | 498 | adv.h.nd_ra_reachable = htonl(iface->ra_reachabletime); 499 | adv.h.nd_ra_retransmit = htonl(iface->ra_retranstime); 500 | 501 | adv.lladdr.type = ND_OPT_SOURCE_LINKADDR; 502 | adv.lladdr.len = 1; 503 | odhcpd_get_mac(iface, adv.lladdr.data); 504 | 505 | adv.mtu.nd_opt_mtu_type = ND_OPT_MTU; 506 | adv.mtu.nd_opt_mtu_len = 1; 507 | 508 | if (mtu == 0) 509 | mtu = odhcpd_get_interface_config(iface->ifname, "mtu"); 510 | 511 | if (mtu < 1280) 512 | mtu = 1280; 513 | 514 | adv.mtu.nd_opt_mtu_mtu = htonl(mtu); 515 | 516 | iov[IOV_RA_ADV].iov_base = (char *)&adv; 517 | iov[IOV_RA_ADV].iov_len = sizeof(adv); 518 | 519 | valid_addr_cnt = (iface->timer_rs.cb /* if not shutdown */ ? iface->addr6_len : 0); 520 | invalid_addr_cnt = iface->invalid_addr6_len; 521 | 522 | // check ra_default 523 | if (iface->default_router) { 524 | default_route = true; 525 | 526 | if (iface->default_router > 1) 527 | valid_prefix = true; 528 | } 529 | 530 | if (valid_addr_cnt + invalid_addr_cnt) { 531 | addrs = alloca(sizeof(*addrs) * (valid_addr_cnt + invalid_addr_cnt)); 532 | 533 | if (valid_addr_cnt) { 534 | memcpy(addrs, iface->addr6, sizeof(*addrs) * valid_addr_cnt); 535 | 536 | /* Check default route */ 537 | if (!default_route && parse_routes(addrs, valid_addr_cnt)) 538 | default_route = true; 539 | } 540 | 541 | if (invalid_addr_cnt) { 542 | size_t i = 0; 543 | 544 | memcpy(&addrs[valid_addr_cnt], iface->invalid_addr6, sizeof(*addrs) * invalid_addr_cnt); 545 | 546 | /* Remove invalid prefixes that were advertised 3 times */ 547 | while (i < iface->invalid_addr6_len) { 548 | if (++iface->invalid_addr6[i].invalid_advertisements >= 3) { 549 | if (i + 1 < iface->invalid_addr6_len) 550 | memmove(&iface->invalid_addr6[i], &iface->invalid_addr6[i + 1], sizeof(*addrs) * (iface->invalid_addr6_len - i - 1)); 551 | 552 | iface->invalid_addr6_len--; 553 | 554 | if (iface->invalid_addr6_len) { 555 | struct odhcpd_ipaddr *new_invalid_addr6 = realloc(iface->invalid_addr6, sizeof(*addrs) * iface->invalid_addr6_len); 556 | 557 | if (new_invalid_addr6) 558 | iface->invalid_addr6 = new_invalid_addr6; 559 | } else { 560 | free(iface->invalid_addr6); 561 | iface->invalid_addr6 = NULL; 562 | } 563 | } else 564 | ++i; 565 | } 566 | } 567 | } 568 | 569 | /* Construct Prefix Information options */ 570 | for (ssize_t i = 0; i < valid_addr_cnt + invalid_addr_cnt; ++i) { 571 | struct odhcpd_ipaddr *addr = &addrs[i]; 572 | struct nd_opt_prefix_info *p = NULL; 573 | uint32_t preferred_lt = 0; 574 | uint32_t valid_lt = 0; 575 | 576 | if (addr->prefix > 96 || (i < valid_addr_cnt && addr->valid_lt <= (uint32_t)now)) { 577 | syslog(LOG_INFO, "Address %s (prefix %d, valid-lifetime %u) not suitable as RA prefix on %s", 578 | inet_ntop(AF_INET6, &addr->addr.in6, buf, sizeof(buf)), addr->prefix, 579 | addr->valid_lt, iface->name); 580 | continue; 581 | } 582 | 583 | if (ADDR_MATCH_PIO_FILTER(addr, iface)) { 584 | syslog(LOG_INFO, "Address %s filtered out as RA prefix on %s", 585 | inet_ntop(AF_INET6, &addr->addr.in6, buf, sizeof(buf)), 586 | iface->name); 587 | continue; /* PIO filtered out of this RA */ 588 | } 589 | 590 | for (size_t i = 0; i < pfxs_cnt; ++i) { 591 | if (addr->prefix == pfxs[i].nd_opt_pi_prefix_len && 592 | !odhcpd_bmemcmp(&pfxs[i].nd_opt_pi_prefix, 593 | &addr->addr.in6, addr->prefix)) 594 | p = &pfxs[i]; 595 | } 596 | 597 | if (!p) { 598 | struct nd_opt_prefix_info *tmp; 599 | 600 | tmp = realloc(pfxs, sizeof(*pfxs) * (pfxs_cnt + 1)); 601 | if (!tmp) { 602 | syslog(LOG_ERR, "Realloc failed for RA prefix option on %s", iface->name); 603 | continue; 604 | } 605 | 606 | pfxs = tmp; 607 | p = &pfxs[pfxs_cnt++]; 608 | memset(p, 0, sizeof(*p)); 609 | } 610 | 611 | if (addr->preferred_lt > (uint32_t)now) { 612 | preferred_lt = TIME_LEFT(addr->preferred_lt, now); 613 | 614 | if (preferred_lt > iface->preferred_lifetime) { 615 | /* set to possibly user mandated preferred_lt */ 616 | preferred_lt = iface->preferred_lifetime; 617 | } 618 | } 619 | 620 | if (addr->valid_lt > (uint32_t)now) { 621 | valid_lt = TIME_LEFT(addr->valid_lt, now); 622 | 623 | if (iface->ra_useleasetime && valid_lt > iface->dhcp_leasetime) 624 | valid_lt = iface->dhcp_leasetime; 625 | } 626 | 627 | if (preferred_lt > valid_lt) { 628 | /* 629 | * RFC4861 § 6.2.1 630 | * This value [AdvPreferredLifetime] MUST NOT be larger than 631 | * AdvValidLifetime. 632 | */ 633 | preferred_lt = valid_lt; 634 | } 635 | 636 | if (lowest_found_lifetime > valid_lt) 637 | lowest_found_lifetime = valid_lt; 638 | 639 | if ((!IN6_IS_ADDR_ULA(&addr->addr.in6) || iface->default_router) && valid_lt) 640 | valid_prefix = true; 641 | 642 | odhcpd_bmemcpy(&p->nd_opt_pi_prefix, &addr->addr.in6, 643 | (iface->ra_advrouter) ? 128 : addr->prefix); 644 | p->nd_opt_pi_type = ND_OPT_PREFIX_INFORMATION; 645 | p->nd_opt_pi_len = 4; 646 | p->nd_opt_pi_prefix_len = (addr->prefix < 64) ? 64 : addr->prefix; 647 | p->nd_opt_pi_flags_reserved = 0; 648 | if (!iface->ra_not_onlink) 649 | p->nd_opt_pi_flags_reserved |= ND_OPT_PI_FLAG_ONLINK; 650 | if (iface->ra_slaac && addr->prefix <= 64) 651 | p->nd_opt_pi_flags_reserved |= ND_OPT_PI_FLAG_AUTO; 652 | if (iface->ra_advrouter) 653 | p->nd_opt_pi_flags_reserved |= ND_OPT_PI_FLAG_RADDR; 654 | p->nd_opt_pi_preferred_time = htonl(preferred_lt); 655 | p->nd_opt_pi_valid_time = htonl(valid_lt); 656 | } 657 | 658 | iov[IOV_RA_PFXS].iov_base = (char *)pfxs; 659 | iov[IOV_RA_PFXS].iov_len = pfxs_cnt * sizeof(*pfxs); 660 | 661 | /* Calculate periodic transmit */ 662 | msecs = calc_adv_interval(iface, lowest_found_lifetime, &maxival); 663 | lifetime = calc_ra_lifetime(iface, maxival); 664 | 665 | if (!iface->have_link_local) { 666 | syslog(LOG_NOTICE, "Skip sending a RA on %s as no link local address is available", iface->name); 667 | goto out; 668 | } 669 | 670 | if (default_route && valid_prefix) { 671 | adv.h.nd_ra_router_lifetime = htons(lifetime < UINT16_MAX ? lifetime : UINT16_MAX); 672 | } else { 673 | adv.h.nd_ra_router_lifetime = 0; 674 | 675 | if (default_route) { 676 | syslog(LOG_WARNING, "A default route is present but there is no public prefix " 677 | "on %s thus we announce no default route by overriding ra_lifetime to 0!", iface->name); 678 | } else { 679 | syslog(LOG_WARNING, "No default route present, overriding ra_lifetime to 0!"); 680 | } 681 | } 682 | 683 | syslog(LOG_DEBUG, "Using a RA lifetime of %d seconds on %s", ntohs(adv.h.nd_ra_router_lifetime), iface->name); 684 | 685 | /* DNS options */ 686 | if (iface->ra_dns) { 687 | struct in6_addr dns_pref, *dns_addr = NULL; 688 | size_t dns_cnt = 0, search_len = iface->search_len; 689 | uint8_t *search_domain = iface->search; 690 | uint8_t search_buf[256]; 691 | 692 | /* DNS Recursive DNS aka RDNSS Type 25; RFC8106 */ 693 | if (iface->dns_cnt > 0) { 694 | dns_addr = iface->dns; 695 | dns_cnt = iface->dns_cnt; 696 | } else if (!odhcpd_get_interface_dns_addr(iface, &dns_pref)) { 697 | dns_addr = &dns_pref; 698 | dns_cnt = 1; 699 | } 700 | 701 | if (dns_cnt) { 702 | dns_sz = sizeof(*dns) + sizeof(struct in6_addr)*dns_cnt; 703 | 704 | dns = alloca(dns_sz); 705 | memset(dns, 0, dns_sz); 706 | dns->type = ND_OPT_RECURSIVE_DNS; 707 | dns->len = 1 + (2 * dns_cnt); 708 | dns->lifetime = htonl(lifetime); 709 | memcpy(dns->addr, dns_addr, sizeof(struct in6_addr)*dns_cnt); 710 | } 711 | 712 | /* DNS Search options aka DNSSL Type 31; RFC8106 */ 713 | if (!search_domain && !res_init() && _res.dnsrch[0] && _res.dnsrch[0][0]) { 714 | int len = dn_comp(_res.dnsrch[0], search_buf, 715 | sizeof(search_buf), NULL, NULL); 716 | if (len > 0) { 717 | search_domain = search_buf; 718 | search_len = len; 719 | } 720 | } 721 | 722 | if (search_len > 0) { 723 | size_t search_padded = ((search_len + 7) & (~7)) + 8; 724 | 725 | search_sz = sizeof(*search) + search_padded; 726 | 727 | search = alloca(search_sz); 728 | memset(search, 0, search_sz); 729 | search->type = ND_OPT_DNS_SEARCH; 730 | search->len = search_len ? ((sizeof(*search) + search_padded) / 8) : 0; 731 | search->lifetime = htonl(lifetime); 732 | memcpy(search->name, search_domain, search_len); 733 | memset(&search->name[search_len], 0, search_padded - search_len); 734 | } 735 | } 736 | 737 | iov[IOV_RA_DNS].iov_base = (char *)dns; 738 | iov[IOV_RA_DNS].iov_len = dns_sz; 739 | iov[IOV_RA_SEARCH].iov_base = (char *)search; 740 | iov[IOV_RA_SEARCH].iov_len = search_sz; 741 | 742 | if (iface->pref64_length) { 743 | /* RFC 8781 § 4.1 rounding up lifetime to multiple of 8 */ 744 | uint16_t pref64_lifetime = lifetime < (UINT16_MAX - 7) ? lifetime + 7 : UINT16_MAX; 745 | 746 | pref64_sz = sizeof(*pref64); 747 | pref64 = alloca(pref64_sz); 748 | pref64->type = ND_OPT_PREF64; 749 | pref64->len = 2; 750 | pref64->lifetime_plc = htons((0xfff8 & pref64_lifetime) | 751 | (0x7 & iface->pref64_plc)); 752 | memcpy(pref64->prefix, iface->pref64_prefix, sizeof(pref64->prefix)); 753 | } 754 | iov[IOV_RA_PREF64].iov_base = (char *)pref64; 755 | iov[IOV_RA_PREF64].iov_len = pref64_sz; 756 | 757 | if (iface->dnr_cnt) { 758 | size_t dnr_sz[iface->dnr_cnt]; 759 | 760 | for (unsigned i = 0; i < iface->dnr_cnt; i++) { 761 | dnr_sz[i] = sizeof(struct nd_opt_dnr_info) + iface->dnr[i].adn_len; 762 | if (iface->dnr[i].addr6_cnt > 0 || iface->dnr[i].svc_len > 0) { 763 | dnr_sz[i] += 2 + iface->dnr[i].addr6_cnt * sizeof(struct in6_addr); 764 | dnr_sz[i] += 2 + iface->dnr[i].svc_len; 765 | } 766 | dnr_sz[i] = (dnr_sz[i] + 7) & ~7; 767 | dnrs_sz += dnr_sz[i]; 768 | } 769 | 770 | /* dnrs are sized in multiples of 8, so each dnr should be aligned */ 771 | dnrs = alloca(dnrs_sz); 772 | memset(dnrs, 0, dnrs_sz); 773 | 774 | uint8_t *pos = (uint8_t *)dnrs; 775 | for (unsigned i = 0; i < iface->dnr_cnt; pos += dnr_sz[i], i++) { 776 | struct nd_opt_dnr_info *dnr = (struct nd_opt_dnr_info *)pos; 777 | size_t dnr_addr6_sz = iface->dnr[i].addr6_cnt * sizeof(struct in6_addr); 778 | uint8_t *tmp = dnr->body; 779 | 780 | dnr->type = ND_OPT_DNR; 781 | dnr->len = dnr_sz[i] / 8; 782 | dnr->priority = htons(iface->dnr[i].priority); 783 | if (iface->dnr[i].lifetime_set) 784 | dnr->lifetime = htonl(iface->dnr[i].lifetime); 785 | else 786 | dnr->lifetime = htonl(lifetime); 787 | 788 | dnr->adn_len = htons(iface->dnr[i].adn_len); 789 | memcpy(tmp, iface->dnr[i].adn, iface->dnr[i].adn_len); 790 | tmp += iface->dnr[i].adn_len; 791 | 792 | *(tmp++) = dnr_addr6_sz >> 8; 793 | *(tmp++) = dnr_addr6_sz & 0xff; 794 | memcpy(tmp, iface->dnr[i].addr6, dnr_addr6_sz); 795 | tmp += dnr_addr6_sz; 796 | 797 | *(tmp++) = iface->dnr[i].svc_len >> 8; 798 | *(tmp++) = iface->dnr[i].svc_len & 0xff; 799 | memcpy(tmp, iface->dnr[i].svc, iface->dnr[i].svc_len); 800 | } 801 | } 802 | iov[IOV_RA_DNR].iov_base = (char *)dnrs; 803 | iov[IOV_RA_DNR].iov_len = dnrs_sz; 804 | 805 | /* 806 | * RFC7084 § 4.3 : 807 | * L-3: An IPv6 CE router MUST advertise itself as a router for the 808 | * delegated prefix(es) (and ULA prefix if configured to provide 809 | * ULA addressing) using the "Route Information Option" specified 810 | * in Section 2.3 of [RFC4191]. This advertisement is 811 | * independent of having or not having IPv6 connectivity on the 812 | * WAN interface. 813 | */ 814 | 815 | for (ssize_t i = 0; i < valid_addr_cnt; ++i) { 816 | struct odhcpd_ipaddr *addr = &addrs[i]; 817 | struct nd_opt_route_info *tmp; 818 | uint32_t valid_lt; 819 | 820 | if (addr->dprefix >= 64 || addr->dprefix == 0 || addr->valid_lt <= (uint32_t)now) { 821 | syslog(LOG_INFO, "Address %s (dprefix %d, valid-lifetime %u) not suitable as RA route on %s", 822 | inet_ntop(AF_INET6, &addr->addr.in6, buf, sizeof(buf)), 823 | addr->dprefix, addr->valid_lt, iface->name); 824 | 825 | continue; /* Address not suitable */ 826 | } 827 | 828 | if (ADDR_MATCH_PIO_FILTER(addr, iface)) { 829 | syslog(LOG_INFO, "Address %s filtered out as RA route on %s", 830 | inet_ntop(AF_INET6, &addr->addr.in6, buf, sizeof(buf)), 831 | iface->name); 832 | continue; /* PIO filtered out of this RA */ 833 | } 834 | 835 | if (addr->dprefix > 32) { 836 | addr->addr.in6.s6_addr32[1] &= htonl(~((1U << (64 - addr->dprefix)) - 1)); 837 | } else if (addr->dprefix <= 32) { 838 | addr->addr.in6.s6_addr32[0] &= htonl(~((1U << (32 - addr->dprefix)) - 1)); 839 | addr->addr.in6.s6_addr32[1] = 0; 840 | } 841 | 842 | tmp = realloc(routes, sizeof(*routes) * (routes_cnt + 1)); 843 | if (!tmp) { 844 | syslog(LOG_ERR, "Realloc failed for RA route option on %s", iface->name); 845 | continue; 846 | } 847 | 848 | routes = tmp; 849 | 850 | memset(&routes[routes_cnt], 0, sizeof(*routes)); 851 | routes[routes_cnt].type = ND_OPT_ROUTE_INFO; 852 | routes[routes_cnt].len = sizeof(*routes) / 8; 853 | routes[routes_cnt].prefix = addr->dprefix; 854 | routes[routes_cnt].flags = 0; 855 | if (iface->route_preference < 0) 856 | routes[routes_cnt].flags |= ND_RA_PREF_LOW; 857 | else if (iface->route_preference > 0) 858 | routes[routes_cnt].flags |= ND_RA_PREF_HIGH; 859 | 860 | valid_lt = TIME_LEFT(addr->valid_lt, now); 861 | routes[routes_cnt].lifetime = htonl(valid_lt < lifetime ? valid_lt : lifetime); 862 | routes[routes_cnt].addr[0] = addr->addr.in6.s6_addr32[0]; 863 | routes[routes_cnt].addr[1] = addr->addr.in6.s6_addr32[1]; 864 | routes[routes_cnt].addr[2] = 0; 865 | routes[routes_cnt].addr[3] = 0; 866 | 867 | ++routes_cnt; 868 | } 869 | 870 | iov[IOV_RA_ROUTES].iov_base = (char *)routes; 871 | iov[IOV_RA_ROUTES].iov_len = routes_cnt * sizeof(*routes); 872 | 873 | memset(&adv_interval, 0, sizeof(adv_interval)); 874 | adv_interval.nd_opt_adv_interval_type = ND_OPT_RTR_ADV_INTERVAL; 875 | adv_interval.nd_opt_adv_interval_len = 1; 876 | adv_interval.nd_opt_adv_interval_ival = htonl(maxival*1000); 877 | 878 | iov[IOV_RA_ADV_INTERVAL].iov_base = (char *)&adv_interval; 879 | iov[IOV_RA_ADV_INTERVAL].iov_len = adv_interval.nd_opt_adv_interval_len * 8; 880 | 881 | memset(&dest, 0, sizeof(dest)); 882 | dest.sin6_family = AF_INET6; 883 | 884 | if (from && !IN6_IS_ADDR_UNSPECIFIED(from)) 885 | dest.sin6_addr = *from; 886 | else 887 | inet_pton(AF_INET6, ALL_IPV6_NODES, &dest.sin6_addr); 888 | 889 | syslog(LOG_NOTICE, "Sending a RA on %s", iface->name); 890 | 891 | if (odhcpd_send(iface->router_event.uloop.fd, &dest, iov, ARRAY_SIZE(iov), iface) > 0) 892 | iface->ra_sent++; 893 | 894 | out: 895 | free(pfxs); 896 | free(routes); 897 | 898 | return msecs; 899 | } 900 | 901 | 902 | static void trigger_router_advert(struct uloop_timeout *event) 903 | { 904 | struct interface *iface = container_of(event, struct interface, timer_rs); 905 | int msecs = send_router_advert(iface, NULL); 906 | 907 | /* Rearm timer if not shut down */ 908 | if (event->cb) 909 | uloop_timeout_set(event, msecs); 910 | } 911 | 912 | 913 | /* Event handler for incoming ICMPv6 packets */ 914 | static void handle_icmpv6(void *addr, void *data, size_t len, 915 | struct interface *iface, _unused void *dest) 916 | { 917 | struct icmp6_hdr *hdr = data; 918 | struct sockaddr_in6 *from = addr; 919 | 920 | if (!router_icmpv6_valid(addr, data, len)) 921 | return; 922 | 923 | if ((iface->ra == MODE_SERVER && !iface->master)) { /* Server mode */ 924 | if (hdr->icmp6_type == ND_ROUTER_SOLICIT) 925 | send_router_advert(iface, &from->sin6_addr); 926 | } else if (iface->ra == MODE_RELAY) { /* Relay mode */ 927 | if (hdr->icmp6_type == ND_ROUTER_SOLICIT && !iface->master) { 928 | struct interface *c; 929 | 930 | avl_for_each_element(&interfaces, c, avl) { 931 | if (!c->master || c->ra != MODE_RELAY) 932 | continue; 933 | 934 | forward_router_solicitation(c); 935 | } 936 | } else if (hdr->icmp6_type == ND_ROUTER_ADVERT && iface->master) 937 | forward_router_advertisement(iface, data, len); 938 | } 939 | } 940 | 941 | 942 | /* Forward router solicitation */ 943 | static void forward_router_solicitation(const struct interface *iface) 944 | { 945 | struct icmp6_hdr rs = {ND_ROUTER_SOLICIT, 0, 0, {{0}}}; 946 | struct iovec iov = {&rs, sizeof(rs)}; 947 | struct sockaddr_in6 all_routers; 948 | 949 | if (!iface) 950 | return; 951 | 952 | memset(&all_routers, 0, sizeof(all_routers)); 953 | all_routers.sin6_family = AF_INET6; 954 | inet_pton(AF_INET6, ALL_IPV6_ROUTERS, &all_routers.sin6_addr); 955 | all_routers.sin6_scope_id = iface->ifindex; 956 | 957 | syslog(LOG_NOTICE, "Sending RS to %s", iface->name); 958 | odhcpd_send(iface->router_event.uloop.fd, &all_routers, &iov, 1, iface); 959 | } 960 | 961 | 962 | /* Handler for incoming router solicitations on slave interfaces */ 963 | static void forward_router_advertisement(const struct interface *iface, uint8_t *data, size_t len) 964 | { 965 | struct nd_router_advert *adv = (struct nd_router_advert *)data; 966 | struct sockaddr_in6 all_nodes; 967 | struct icmpv6_opt *opt; 968 | struct interface *c; 969 | struct iovec iov = { .iov_base = data, .iov_len = len }; 970 | /* Rewrite options */ 971 | uint8_t *end = data + len; 972 | uint8_t *mac_ptr = NULL; 973 | struct in6_addr *dns_ptr = NULL; 974 | size_t dns_count = 0; 975 | 976 | icmpv6_for_each_option(opt, &adv[1], end) { 977 | if (opt->type == ND_OPT_SOURCE_LINKADDR) { 978 | /* Store address of source MAC-address */ 979 | mac_ptr = opt->data; 980 | } else if (opt->type == ND_OPT_RECURSIVE_DNS && opt->len > 1) { 981 | /* Check if we have to rewrite DNS */ 982 | dns_ptr = (struct in6_addr*)&opt->data[6]; 983 | dns_count = (opt->len - 1) / 2; 984 | } 985 | } 986 | 987 | syslog(LOG_NOTICE, "Got a RA on %s", iface->name); 988 | 989 | /* Indicate a proxy, however we don't follow the rest of RFC 4389 yet */ 990 | adv->nd_ra_flags_reserved |= ND_RA_FLAG_PROXY; 991 | 992 | /* Forward advertisement to all slave interfaces */ 993 | memset(&all_nodes, 0, sizeof(all_nodes)); 994 | all_nodes.sin6_family = AF_INET6; 995 | inet_pton(AF_INET6, ALL_IPV6_NODES, &all_nodes.sin6_addr); 996 | 997 | avl_for_each_element(&interfaces, c, avl) { 998 | if (c->ra != MODE_RELAY || c->master) 999 | continue; 1000 | 1001 | /* Fixup source hardware address option */ 1002 | if (mac_ptr) 1003 | odhcpd_get_mac(c, mac_ptr); 1004 | 1005 | /* If we have to rewrite DNS entries */ 1006 | if (c->always_rewrite_dns && dns_ptr && dns_count > 0) { 1007 | const struct in6_addr *rewrite = c->dns; 1008 | struct in6_addr addr; 1009 | size_t rewrite_cnt = c->dns_cnt; 1010 | 1011 | if (rewrite_cnt == 0) { 1012 | if (odhcpd_get_interface_dns_addr(c, &addr)) 1013 | continue; /* Unable to comply */ 1014 | 1015 | rewrite = &addr; 1016 | rewrite_cnt = 1; 1017 | } 1018 | 1019 | /* Copy over any other addresses */ 1020 | for (size_t i = 0; i < dns_count; ++i) { 1021 | size_t j = (i < rewrite_cnt) ? i : rewrite_cnt - 1; 1022 | dns_ptr[i] = rewrite[j]; 1023 | } 1024 | } 1025 | 1026 | syslog(LOG_NOTICE, "Forward a RA on %s", c->name); 1027 | 1028 | odhcpd_send(c->router_event.uloop.fd, &all_nodes, &iov, 1, c); 1029 | } 1030 | } 1031 | -------------------------------------------------------------------------------- /src/router.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2012-2013 Steven Barth 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License v2 as published by 6 | * the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | * 13 | */ 14 | 15 | #pragma once 16 | #include 17 | #include 18 | #include 19 | 20 | struct icmpv6_opt { 21 | uint8_t type; 22 | uint8_t len; 23 | uint8_t data[6]; 24 | }; 25 | 26 | 27 | #define icmpv6_for_each_option(opt, start, end)\ 28 | for (opt = (struct icmpv6_opt*)(start);\ 29 | (void*)(opt + 1) <= (void*)(end) && opt->len > 0 &&\ 30 | (void*)(opt + opt->len) <= (void*)(end); opt += opt->len) 31 | 32 | 33 | #define MaxInitialRtrAdvInterval 16 34 | #define MaxInitialRtAdvs 3 35 | #define MaxRtrAdvInterval 1800 36 | #define MinRtrAdvInterval 3 37 | 38 | #define ND_RA_FLAG_PROXY 0x4 39 | #define ND_RA_PREF_HIGH (1 << 3) 40 | #define ND_RA_PREF_LOW (3 << 3) 41 | -------------------------------------------------------------------------------- /src/ubus.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | #include "odhcpd.h" 11 | #include "dhcpv6.h" 12 | #include "dhcpv4.h" 13 | 14 | static struct ubus_context *ubus = NULL; 15 | static struct ubus_subscriber netifd; 16 | static struct blob_buf b; 17 | static struct blob_attr *dump = NULL; 18 | static uint32_t objid = 0; 19 | static struct ubus_request req_dump = { .list = LIST_HEAD_INIT(req_dump.list) }; 20 | 21 | static int handle_dhcpv4_leases(struct ubus_context *ctx, _unused struct ubus_object *obj, 22 | struct ubus_request_data *req, _unused const char *method, 23 | _unused struct blob_attr *msg) 24 | { 25 | struct interface *iface; 26 | time_t now = odhcpd_time(); 27 | void *a; 28 | 29 | blob_buf_init(&b, 0); 30 | a = blobmsg_open_table(&b, "device"); 31 | 32 | avl_for_each_element(&interfaces, iface, avl) { 33 | if (iface->dhcpv4 != MODE_SERVER) 34 | continue; 35 | 36 | void *i = blobmsg_open_table(&b, iface->ifname); 37 | void *j = blobmsg_open_array(&b, "leases"); 38 | 39 | struct dhcp_assignment *c; 40 | list_for_each_entry(c, &iface->dhcpv4_assignments, head) { 41 | if (!INFINITE_VALID(c->valid_until) && c->valid_until < now) 42 | continue; 43 | 44 | void *m, *l = blobmsg_open_table(&b, NULL); 45 | char *buf = blobmsg_alloc_string_buffer(&b, "mac", 13); 46 | 47 | odhcpd_hexlify(buf, c->hwaddr, sizeof(c->hwaddr)); 48 | blobmsg_add_string_buffer(&b); 49 | 50 | blobmsg_add_string(&b, "hostname", (c->hostname) ? c->hostname : ""); 51 | blobmsg_add_u8(&b, "accept-reconf-nonce", c->accept_fr_nonce); 52 | 53 | if (c->reqopts_len > 0) { 54 | buf = blobmsg_alloc_string_buffer(&b, "reqopts", c->reqopts_len * 4 + 1); 55 | for (size_t i = 0; i < c->reqopts_len; i++) 56 | buf += snprintf(buf, 5, "%" PRIu8 ",", c->reqopts[i]); 57 | buf[-1] = '\0'; 58 | blobmsg_add_string_buffer(&b); 59 | } 60 | 61 | m = blobmsg_open_array(&b, "flags"); 62 | if (c->flags & OAF_BOUND) 63 | blobmsg_add_string(&b, NULL, "bound"); 64 | 65 | if (c->flags & OAF_STATIC) 66 | blobmsg_add_string(&b, NULL, "static"); 67 | 68 | if (c->flags & OAF_BROKEN_HOSTNAME) 69 | blobmsg_add_string(&b, NULL, "broken-hostname"); 70 | blobmsg_close_array(&b, m); 71 | 72 | buf = blobmsg_alloc_string_buffer(&b, "address", INET_ADDRSTRLEN); 73 | struct in_addr addr = {.s_addr = c->addr}; 74 | inet_ntop(AF_INET, &addr, buf, INET_ADDRSTRLEN); 75 | blobmsg_add_string_buffer(&b); 76 | 77 | blobmsg_add_u32(&b, "valid", INFINITE_VALID(c->valid_until) ? 78 | (uint32_t)-1 : (uint32_t)(c->valid_until - now)); 79 | 80 | blobmsg_close_table(&b, l); 81 | } 82 | 83 | blobmsg_close_array(&b, j); 84 | blobmsg_close_table(&b, i); 85 | } 86 | 87 | blobmsg_close_table(&b, a); 88 | ubus_send_reply(ctx, req, b.head); 89 | 90 | return 0; 91 | } 92 | 93 | static void dhcpv6_blobmsg_ia_addr(struct in6_addr *addr, int prefix, uint32_t pref, 94 | uint32_t valid, _unused void *arg) 95 | { 96 | void *a = blobmsg_open_table(&b, NULL); 97 | char *buf = blobmsg_alloc_string_buffer(&b, "address", INET6_ADDRSTRLEN); 98 | 99 | inet_ntop(AF_INET6, addr, buf, INET6_ADDRSTRLEN); 100 | blobmsg_add_string_buffer(&b); 101 | blobmsg_add_u32(&b, "preferred-lifetime", 102 | pref == UINT32_MAX ? (uint32_t)-1 : pref); 103 | blobmsg_add_u32(&b, "valid-lifetime", 104 | valid == UINT32_MAX ? (uint32_t)-1 : valid); 105 | 106 | if (prefix != 128) 107 | blobmsg_add_u32(&b, "prefix-length", prefix); 108 | 109 | blobmsg_close_table(&b, a); 110 | } 111 | 112 | static int handle_dhcpv6_leases(_unused struct ubus_context *ctx, _unused struct ubus_object *obj, 113 | _unused struct ubus_request_data *req, _unused const char *method, 114 | _unused struct blob_attr *msg) 115 | { 116 | struct interface *iface; 117 | time_t now = odhcpd_time(); 118 | void *a; 119 | 120 | blob_buf_init(&b, 0); 121 | a = blobmsg_open_table(&b, "device"); 122 | 123 | avl_for_each_element(&interfaces, iface, avl) { 124 | if (iface->dhcpv6 != MODE_SERVER) 125 | continue; 126 | 127 | void *i = blobmsg_open_table(&b, iface->ifname); 128 | void *j = blobmsg_open_array(&b, "leases"); 129 | 130 | struct dhcp_assignment *a, *border = list_last_entry( 131 | &iface->ia_assignments, struct dhcp_assignment, head); 132 | 133 | list_for_each_entry(a, &iface->ia_assignments, head) { 134 | if (a == border || (!INFINITE_VALID(a->valid_until) && 135 | a->valid_until < now)) 136 | continue; 137 | 138 | void *m, *l = blobmsg_open_table(&b, NULL); 139 | char *buf = blobmsg_alloc_string_buffer(&b, "duid", 264); 140 | 141 | odhcpd_hexlify(buf, a->clid_data, a->clid_len); 142 | blobmsg_add_string_buffer(&b); 143 | 144 | blobmsg_add_u32(&b, "iaid", ntohl(a->iaid)); 145 | blobmsg_add_string(&b, "hostname", (a->hostname) ? a->hostname : ""); 146 | blobmsg_add_u8(&b, "accept-reconf", a->accept_reconf); 147 | if (a->flags & OAF_DHCPV6_NA) 148 | blobmsg_add_u64(&b, "assigned", a->assigned_host_id); 149 | else 150 | blobmsg_add_u16(&b, "assigned", a->assigned_subnet_id); 151 | 152 | m = blobmsg_open_array(&b, "flags"); 153 | if (a->flags & OAF_BOUND) 154 | blobmsg_add_string(&b, NULL, "bound"); 155 | 156 | if (a->flags & OAF_STATIC) 157 | blobmsg_add_string(&b, NULL, "static"); 158 | blobmsg_close_array(&b, m); 159 | 160 | m = blobmsg_open_array(&b, a->flags & OAF_DHCPV6_NA ? "ipv6-addr": "ipv6-prefix"); 161 | dhcpv6_ia_enum_addrs(iface, a, now, dhcpv6_blobmsg_ia_addr, NULL); 162 | blobmsg_close_table(&b, m); 163 | 164 | blobmsg_add_u32(&b, "valid", INFINITE_VALID(a->valid_until) ? 165 | (uint32_t)-1 : (uint32_t)(a->valid_until - now)); 166 | 167 | blobmsg_close_table(&b, l); 168 | } 169 | 170 | blobmsg_close_array(&b, j); 171 | blobmsg_close_table(&b, i); 172 | } 173 | 174 | blobmsg_close_table(&b, a); 175 | ubus_send_reply(ctx, req, b.head); 176 | return 0; 177 | } 178 | 179 | static int handle_add_lease(_unused struct ubus_context *ctx, _unused struct ubus_object *obj, 180 | _unused struct ubus_request_data *req, _unused const char *method, 181 | struct blob_attr *msg) 182 | { 183 | if (!set_lease_from_blobmsg(msg)) 184 | return UBUS_STATUS_OK; 185 | 186 | return UBUS_STATUS_INVALID_ARGUMENT; 187 | } 188 | 189 | static struct ubus_method main_object_methods[] = { 190 | {.name = "ipv4leases", .handler = handle_dhcpv4_leases}, 191 | {.name = "ipv6leases", .handler = handle_dhcpv6_leases}, 192 | UBUS_METHOD("add_lease", handle_add_lease, lease_attrs), 193 | }; 194 | 195 | static struct ubus_object_type main_object_type = 196 | UBUS_OBJECT_TYPE("dhcp", main_object_methods); 197 | 198 | static struct ubus_object main_object = { 199 | .name = "dhcp", 200 | .type = &main_object_type, 201 | .methods = main_object_methods, 202 | .n_methods = ARRAY_SIZE(main_object_methods), 203 | }; 204 | 205 | 206 | enum { 207 | DUMP_ATTR_INTERFACE, 208 | DUMP_ATTR_MAX 209 | }; 210 | 211 | static const struct blobmsg_policy dump_attrs[DUMP_ATTR_MAX] = { 212 | [DUMP_ATTR_INTERFACE] = { .name = "interface", .type = BLOBMSG_TYPE_ARRAY }, 213 | }; 214 | 215 | 216 | enum { 217 | IFACE_ATTR_INTERFACE, 218 | IFACE_ATTR_IFNAME, 219 | IFACE_ATTR_UP, 220 | IFACE_ATTR_DATA, 221 | IFACE_ATTR_PREFIX, 222 | IFACE_ATTR_ADDRESS, 223 | IFACE_ATTR_MAX, 224 | }; 225 | 226 | static const struct blobmsg_policy iface_attrs[IFACE_ATTR_MAX] = { 227 | [IFACE_ATTR_INTERFACE] = { .name = "interface", .type = BLOBMSG_TYPE_STRING }, 228 | [IFACE_ATTR_IFNAME] = { .name = "l3_device", .type = BLOBMSG_TYPE_STRING }, 229 | [IFACE_ATTR_UP] = { .name = "up", .type = BLOBMSG_TYPE_BOOL }, 230 | [IFACE_ATTR_DATA] = { .name = "data", .type = BLOBMSG_TYPE_TABLE }, 231 | [IFACE_ATTR_PREFIX] = { .name = "ipv6-prefix", .type = BLOBMSG_TYPE_ARRAY }, 232 | [IFACE_ATTR_ADDRESS] = { .name = "ipv6-address", .type = BLOBMSG_TYPE_ARRAY }, 233 | }; 234 | 235 | static void handle_dump(_unused struct ubus_request *req, _unused int type, struct blob_attr *msg) 236 | { 237 | struct blob_attr *tb[DUMP_ATTR_MAX]; 238 | blobmsg_parse(dump_attrs, DUMP_ATTR_MAX, tb, blob_data(msg), blob_len(msg)); 239 | 240 | if (!tb[DUMP_ATTR_INTERFACE]) 241 | return; 242 | 243 | free(dump); 244 | dump = blob_memdup(tb[DUMP_ATTR_INTERFACE]); 245 | odhcpd_reload(); 246 | } 247 | 248 | 249 | static void update_netifd(bool subscribe) 250 | { 251 | if (subscribe) 252 | ubus_subscribe(ubus, &netifd, objid); 253 | 254 | ubus_abort_request(ubus, &req_dump); 255 | blob_buf_init(&b, 0); 256 | 257 | if (!ubus_invoke_async(ubus, objid, "dump", b.head, &req_dump)) { 258 | req_dump.data_cb = handle_dump; 259 | ubus_complete_request_async(ubus, &req_dump); 260 | } 261 | } 262 | 263 | 264 | static int handle_update(_unused struct ubus_context *ctx, _unused struct ubus_object *obj, 265 | _unused struct ubus_request_data *req, _unused const char *method, 266 | struct blob_attr *msg) 267 | { 268 | struct blob_attr *tb[IFACE_ATTR_MAX]; 269 | struct interface *c; 270 | bool update = true; 271 | 272 | blobmsg_parse(iface_attrs, IFACE_ATTR_MAX, tb, blob_data(msg), blob_len(msg)); 273 | const char *interface = (tb[IFACE_ATTR_INTERFACE]) ? 274 | blobmsg_get_string(tb[IFACE_ATTR_INTERFACE]) : ""; 275 | 276 | avl_for_each_element(&interfaces, c, avl) { 277 | if (!strcmp(interface, c->name) && c->ignore) { 278 | update = false; 279 | break; 280 | } 281 | } 282 | 283 | if (update) 284 | update_netifd(false); 285 | 286 | return 0; 287 | } 288 | 289 | 290 | void ubus_apply_network(void) 291 | { 292 | struct blob_attr *a; 293 | unsigned rem; 294 | 295 | if (!dump) 296 | return; 297 | 298 | blobmsg_for_each_attr(a, dump, rem) { 299 | struct blob_attr *tb[IFACE_ATTR_MAX]; 300 | blobmsg_parse(iface_attrs, IFACE_ATTR_MAX, tb, blobmsg_data(a), blobmsg_data_len(a)); 301 | 302 | if (!tb[IFACE_ATTR_INTERFACE] || !tb[IFACE_ATTR_DATA]) 303 | continue; 304 | 305 | const char *interface = (tb[IFACE_ATTR_INTERFACE]) ? 306 | blobmsg_get_string(tb[IFACE_ATTR_INTERFACE]) : ""; 307 | 308 | bool matched = false; 309 | struct interface *c, *tmp; 310 | avl_for_each_element_safe(&interfaces, c, avl, tmp) { 311 | char *f = memmem(c->upstream, c->upstream_len, 312 | interface, strlen(interface) + 1); 313 | bool cmatched = !strcmp(interface, c->name); 314 | matched |= cmatched; 315 | 316 | if (!cmatched && (!c->upstream_len || !f || (f != c->upstream && f[-1] != 0))) 317 | continue; 318 | 319 | if (!c->ignore) 320 | config_parse_interface(blobmsg_data(tb[IFACE_ATTR_DATA]), 321 | blobmsg_data_len(tb[IFACE_ATTR_DATA]), c->name, false); 322 | } 323 | 324 | if (!matched) 325 | config_parse_interface(blobmsg_data(tb[IFACE_ATTR_DATA]), 326 | blobmsg_data_len(tb[IFACE_ATTR_DATA]), interface, false); 327 | } 328 | } 329 | 330 | 331 | enum { 332 | OBJ_ATTR_ID, 333 | OBJ_ATTR_PATH, 334 | OBJ_ATTR_MAX 335 | }; 336 | 337 | static const struct blobmsg_policy obj_attrs[OBJ_ATTR_MAX] = { 338 | [OBJ_ATTR_ID] = { .name = "id", .type = BLOBMSG_TYPE_INT32 }, 339 | [OBJ_ATTR_PATH] = { .name = "path", .type = BLOBMSG_TYPE_STRING }, 340 | }; 341 | 342 | void ubus_bcast_dhcp_event(const char *type, const uint8_t *mac, 343 | const size_t mlen, const struct in_addr *addr, const char *name, 344 | const char *interface) 345 | { 346 | if (!ubus || !main_object.has_subscribers) 347 | return; 348 | 349 | blob_buf_init(&b, 0); 350 | if (mac) 351 | blobmsg_add_string(&b, "mac", odhcpd_print_mac(mac, mlen)); 352 | if (addr) 353 | blobmsg_add_string(&b, "ip", inet_ntoa(*addr)); 354 | if (name) 355 | blobmsg_add_string(&b, "name", name); 356 | if (interface) 357 | blobmsg_add_string(&b, "interface", interface); 358 | 359 | ubus_notify(ubus, &main_object, type, b.head, -1); 360 | } 361 | 362 | static void handle_event(_unused struct ubus_context *ctx, _unused struct ubus_event_handler *ev, 363 | _unused const char *type, struct blob_attr *msg) 364 | { 365 | struct blob_attr *tb[OBJ_ATTR_MAX]; 366 | blobmsg_parse(obj_attrs, OBJ_ATTR_MAX, tb, blob_data(msg), blob_len(msg)); 367 | 368 | if (!tb[OBJ_ATTR_ID] || !tb[OBJ_ATTR_PATH]) 369 | return; 370 | 371 | if (strcmp(blobmsg_get_string(tb[OBJ_ATTR_PATH]), "network.interface")) 372 | return; 373 | 374 | objid = blobmsg_get_u32(tb[OBJ_ATTR_ID]); 375 | update_netifd(true); 376 | } 377 | 378 | static struct ubus_event_handler event_handler = { .cb = handle_event }; 379 | 380 | 381 | const char* ubus_get_ifname(const char *name) 382 | { 383 | struct blob_attr *c; 384 | unsigned rem; 385 | 386 | if (!dump) 387 | return NULL; 388 | 389 | blobmsg_for_each_attr(c, dump, rem) { 390 | struct blob_attr *tb[IFACE_ATTR_MAX]; 391 | blobmsg_parse(iface_attrs, IFACE_ATTR_MAX, tb, blobmsg_data(c), blobmsg_data_len(c)); 392 | 393 | if (!tb[IFACE_ATTR_INTERFACE] || strcmp(name, 394 | blobmsg_get_string(tb[IFACE_ATTR_INTERFACE]))) 395 | continue; 396 | 397 | if (tb[IFACE_ATTR_IFNAME]) 398 | return blobmsg_get_string(tb[IFACE_ATTR_IFNAME]); 399 | } 400 | 401 | return NULL; 402 | } 403 | 404 | 405 | bool ubus_has_prefix(const char *name, const char *ifname) 406 | { 407 | struct blob_attr *c, *cur; 408 | unsigned rem; 409 | 410 | if (!dump) 411 | return NULL; 412 | 413 | blobmsg_for_each_attr(c, dump, rem) { 414 | struct blob_attr *tb[IFACE_ATTR_MAX]; 415 | blobmsg_parse(iface_attrs, IFACE_ATTR_MAX, tb, blobmsg_data(c), blobmsg_data_len(c)); 416 | 417 | if (!tb[IFACE_ATTR_INTERFACE] || !tb[IFACE_ATTR_IFNAME]) 418 | continue; 419 | 420 | if (strcmp(name, blobmsg_get_string(tb[IFACE_ATTR_INTERFACE])) || 421 | strcmp(ifname, blobmsg_get_string(tb[IFACE_ATTR_IFNAME]))) 422 | continue; 423 | 424 | if ((cur = tb[IFACE_ATTR_PREFIX])) { 425 | if (blobmsg_type(cur) != BLOBMSG_TYPE_ARRAY || !blobmsg_check_attr(cur, false)) 426 | continue; 427 | 428 | struct blob_attr *d; 429 | unsigned drem; 430 | blobmsg_for_each_attr(d, cur, drem) { 431 | return true; 432 | } 433 | } 434 | } 435 | 436 | return false; 437 | } 438 | 439 | 440 | int ubus_init(void) 441 | { 442 | if (!(ubus = ubus_connect(NULL))) { 443 | syslog(LOG_ERR, "Unable to connect to ubus: %m"); 444 | return -1; 445 | } 446 | 447 | netifd.cb = handle_update; 448 | ubus_register_subscriber(ubus, &netifd); 449 | 450 | ubus_add_uloop(ubus); 451 | ubus_add_object(ubus, &main_object); 452 | ubus_register_event_handler(ubus, &event_handler, "ubus.object.add"); 453 | if (!ubus_lookup_id(ubus, "network.interface", &objid)) 454 | update_netifd(true); 455 | 456 | return 0; 457 | } 458 | 459 | --------------------------------------------------------------------------------