├── .gitignore ├── .gitlab-ci.yml ├── CHANGELOG.md ├── LICENSE ├── Makefile ├── README.md ├── examples ├── debug.sh ├── ipv6_prefix_delegation │ ├── README.md │ ├── ipv6-routes.sh │ └── kea-dhcp6.conf └── slash32_leases │ ├── README.md │ ├── kea-dhcp4.conf │ └── script.sh ├── release.sh └── src ├── callouts.cc ├── common.h ├── load.cc ├── logger.cc ├── logger.h ├── messages.cc ├── messages.h ├── messages.mes ├── runscript.cc ├── runscript.h └── version.cc /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.d 3 | *.so 4 | *~ 5 | *.tar.gz 6 | -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | stages: 2 | - build 3 | - collect 4 | 5 | build-debian-ubuntu: 6 | stage: build 7 | image: $DISTRO:$RELEASE 8 | before_script: 9 | - apt update && DEBIAN_FRONTEND=noninteractive apt-get install -y apt-transport-https curl gnupg2 10 | - curl -1sLf "https://dl.cloudsmith.io/public/isc/kea-${KEA_VERSION}/cfg/gpg/gpg.${GPG_KEY}.key" | apt-key add - 11 | - curl -1sLf "https://dl.cloudsmith.io/public/isc/kea-${KEA_VERSION}/cfg/setup/config.deb.txt?distro=${DISTRO}&codename=${RELEASE}" > /etc/apt/sources.list.d/isc-kea.list 12 | - apt update && DEBIAN_FRONTEND=noninteractive apt-get install -y isc-kea-dev libboost-dev g++ make 13 | script: 14 | - make -j4 15 | after_script: 16 | - mv kea-hook-runscript.so kea-hook-runscript-kea-${KEA_VERSION}-${DISTRO}-${RELEASE}.so 17 | rules: 18 | - changes: 19 | - .gitlab-ci.yml 20 | - src/* 21 | - Makefile 22 | - CHANGELOG.md 23 | parallel: 24 | matrix: 25 | - KEA_VERSION: "1-6" 26 | GPG_KEY: "0607E2621F1564A6" 27 | DISTRO: debian 28 | RELEASE: [stretch, buster] 29 | - KEA_VERSION: "1-7" 30 | GPG_KEY: "40544590508A17DE" 31 | DISTRO: debian 32 | RELEASE: [stretch, buster] 33 | - KEA_VERSION: "1-8" 34 | GPG_KEY: "4DD5AE28ADA7268E" 35 | DISTRO: debian 36 | RELEASE: [stretch, buster] 37 | - KEA_VERSION: "1-9" 38 | GPG_KEY: "5DC67B0A74E30739" 39 | DISTRO: debian 40 | RELEASE: [stretch, buster] 41 | - KEA_VERSION: "1-6" 42 | GPG_KEY: "0607E2621F1564A6" 43 | DISTRO: ubuntu 44 | RELEASE: [bionic] 45 | - KEA_VERSION: "1-7" 46 | GPG_KEY: "40544590508A17DE" 47 | DISTRO: ubuntu 48 | RELEASE: [bionic, focal] 49 | - KEA_VERSION: "1-8" 50 | GPG_KEY: "4DD5AE28ADA7268E" 51 | DISTRO: ubuntu 52 | RELEASE: [bionic, focal] 53 | - KEA_VERSION: "1-9" 54 | GPG_KEY: "5DC67B0A74E30739" 55 | DISTRO: ubuntu 56 | RELEASE: [bionic, focal] 57 | artifacts: 58 | paths: 59 | - kea-hook-runscript*.so 60 | expire_in: 2 days 61 | 62 | # Collect all artifacts in the same job: easier to browse 63 | collect-artifacts-snapshot: 64 | stage: collect 65 | allow_failure: true 66 | script: 67 | - ls -lh kea-hook-runscript*.so 68 | artifacts: 69 | paths: 70 | - kea-hook-runscript*.so 71 | expire_in: 15 days 72 | except: 73 | - tags 74 | 75 | # Same for tagged releases, with no expiration 76 | collect-artifacts-release: 77 | stage: collect 78 | script: 79 | - ls -lh kea-hook-runscript*.so 80 | artifacts: 81 | paths: 82 | - kea-hook-runscript*.so 83 | expire_in: never 84 | only: 85 | - tags 86 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog for kea-hook-runscript 2 | 3 | ## 1.4.0 (2021-01-08) 4 | 5 | New features: 6 | 7 | - Add support for DHCP option 82, Relay Agent Information (#22) 8 | - Generate binaries with a [CI system](https://code.ffdn.org/zorun/kea-hook-runscript/-/pipelines) 9 | Currently, Debian and Ubuntu are supported, for Kea versions from 1.6 to 1.9. 10 | Binaries are attached to this release for convenience. 11 | 12 | Fixes: 13 | 14 | - Ship pre-built `messages.{cc,h}` to avoid build dependency on kea-msg-compiler. 15 | This should make it easier to build the hook from git. 16 | 17 | Misc: 18 | 19 | - Add IPv6 prefix delegation example 20 | 21 | ## 1.3.3 (2020-02-27) 22 | 23 | Fixes: 24 | 25 | - Fix a crash in `lease6_expire` when IPv6 leases are reclaimed by Kea (#18) 26 | 27 | ## 1.3.2 (2020-01-31) 28 | 29 | Fixes: 30 | 31 | - Fix MAC address reported in `KEA_QUERY4_HWADDR` when the query is received from a DHCP relay (#17) 32 | 33 | ## 1.3.1 (2019-11-02) 34 | 35 | Misc: 36 | 37 | - Produce release tarballs with pre-built messages, to avoid depending on the `kea-msg-compiler` binary (#15) 38 | 39 | ## 1.3.0 (2019-11-02) 40 | 41 | New features: 42 | 43 | - Add support for DHCP option 60 (#12) 44 | 45 | Fixes: 46 | 47 | - Fix build against Kea 1.6 (#16) 48 | - Fix build with older compilers 49 | 50 | Misc: 51 | 52 | - Licence code under the MPL 53 | 54 | ## 1.2.0 (2019-01-24) 55 | 56 | - Allow to run the script asynchronously (by setting `wait=false`, see `README`) 57 | 58 | ## 1.1.0 (2017-10-22) 59 | 60 | - Add lots of DHCPv6 variables: DHCPv6 should now be usable 61 | - Add some more DHCPv4 variables, especially for leases 62 | - Safety fixes: fix potential NULL pointer dereference, and unsafe string usage 63 | - Makefile: use standard CXX variable instead of hard-coding g++ 64 | 65 | ## 1.0.0 (2017-08-24) 66 | 67 | - Initial release 68 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Mozilla Public License Version 2.0 2 | ================================== 3 | 4 | 1. Definitions 5 | -------------- 6 | 7 | 1.1. "Contributor" 8 | means each individual or legal entity that creates, contributes to 9 | the creation of, or owns Covered Software. 10 | 11 | 1.2. "Contributor Version" 12 | means the combination of the Contributions of others (if any) used 13 | by a Contributor and that particular Contributor's Contribution. 14 | 15 | 1.3. "Contribution" 16 | means Covered Software of a particular Contributor. 17 | 18 | 1.4. "Covered Software" 19 | means Source Code Form to which the initial Contributor has attached 20 | the notice in Exhibit A, the Executable Form of such Source Code 21 | Form, and Modifications of such Source Code Form, in each case 22 | including portions thereof. 23 | 24 | 1.5. "Incompatible With Secondary Licenses" 25 | means 26 | 27 | (a) that the initial Contributor has attached the notice described 28 | in Exhibit B to the Covered Software; or 29 | 30 | (b) that the Covered Software was made available under the terms of 31 | version 1.1 or earlier of the License, but not also under the 32 | terms of a Secondary License. 33 | 34 | 1.6. "Executable Form" 35 | means any form of the work other than Source Code Form. 36 | 37 | 1.7. "Larger Work" 38 | means a work that combines Covered Software with other material, in 39 | a separate file or files, that is not Covered Software. 40 | 41 | 1.8. "License" 42 | means this document. 43 | 44 | 1.9. "Licensable" 45 | means having the right to grant, to the maximum extent possible, 46 | whether at the time of the initial grant or subsequently, any and 47 | all of the rights conveyed by this License. 48 | 49 | 1.10. "Modifications" 50 | means any of the following: 51 | 52 | (a) any file in Source Code Form that results from an addition to, 53 | deletion from, or modification of the contents of Covered 54 | Software; or 55 | 56 | (b) any new file in Source Code Form that contains any Covered 57 | Software. 58 | 59 | 1.11. "Patent Claims" of a Contributor 60 | means any patent claim(s), including without limitation, method, 61 | process, and apparatus claims, in any patent Licensable by such 62 | Contributor that would be infringed, but for the grant of the 63 | License, by the making, using, selling, offering for sale, having 64 | made, import, or transfer of either its Contributions or its 65 | Contributor Version. 66 | 67 | 1.12. "Secondary License" 68 | means either the GNU General Public License, Version 2.0, the GNU 69 | Lesser General Public License, Version 2.1, the GNU Affero General 70 | Public License, Version 3.0, or any later versions of those 71 | licenses. 72 | 73 | 1.13. "Source Code Form" 74 | means the form of the work preferred for making modifications. 75 | 76 | 1.14. "You" (or "Your") 77 | means an individual or a legal entity exercising rights under this 78 | License. For legal entities, "You" includes any entity that 79 | controls, is controlled by, or is under common control with You. For 80 | purposes of this definition, "control" means (a) the power, direct 81 | or indirect, to cause the direction or management of such entity, 82 | whether by contract or otherwise, or (b) ownership of more than 83 | fifty percent (50%) of the outstanding shares or beneficial 84 | ownership of such entity. 85 | 86 | 2. License Grants and Conditions 87 | -------------------------------- 88 | 89 | 2.1. Grants 90 | 91 | Each Contributor hereby grants You a world-wide, royalty-free, 92 | non-exclusive license: 93 | 94 | (a) under intellectual property rights (other than patent or trademark) 95 | Licensable by such Contributor to use, reproduce, make available, 96 | modify, display, perform, distribute, and otherwise exploit its 97 | Contributions, either on an unmodified basis, with Modifications, or 98 | as part of a Larger Work; and 99 | 100 | (b) under Patent Claims of such Contributor to make, use, sell, offer 101 | for sale, have made, import, and otherwise transfer either its 102 | Contributions or its Contributor Version. 103 | 104 | 2.2. Effective Date 105 | 106 | The licenses granted in Section 2.1 with respect to any Contribution 107 | become effective for each Contribution on the date the Contributor first 108 | distributes such Contribution. 109 | 110 | 2.3. Limitations on Grant Scope 111 | 112 | The licenses granted in this Section 2 are the only rights granted under 113 | this License. No additional rights or licenses will be implied from the 114 | distribution or licensing of Covered Software under this License. 115 | Notwithstanding Section 2.1(b) above, no patent license is granted by a 116 | Contributor: 117 | 118 | (a) for any code that a Contributor has removed from Covered Software; 119 | or 120 | 121 | (b) for infringements caused by: (i) Your and any other third party's 122 | modifications of Covered Software, or (ii) the combination of its 123 | Contributions with other software (except as part of its Contributor 124 | Version); or 125 | 126 | (c) under Patent Claims infringed by Covered Software in the absence of 127 | its Contributions. 128 | 129 | This License does not grant any rights in the trademarks, service marks, 130 | or logos of any Contributor (except as may be necessary to comply with 131 | the notice requirements in Section 3.4). 132 | 133 | 2.4. Subsequent Licenses 134 | 135 | No Contributor makes additional grants as a result of Your choice to 136 | distribute the Covered Software under a subsequent version of this 137 | License (see Section 10.2) or under the terms of a Secondary License (if 138 | permitted under the terms of Section 3.3). 139 | 140 | 2.5. Representation 141 | 142 | Each Contributor represents that the Contributor believes its 143 | Contributions are its original creation(s) or it has sufficient rights 144 | to grant the rights to its Contributions conveyed by this License. 145 | 146 | 2.6. Fair Use 147 | 148 | This License is not intended to limit any rights You have under 149 | applicable copyright doctrines of fair use, fair dealing, or other 150 | equivalents. 151 | 152 | 2.7. Conditions 153 | 154 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted 155 | in Section 2.1. 156 | 157 | 3. Responsibilities 158 | ------------------- 159 | 160 | 3.1. Distribution of Source Form 161 | 162 | All distribution of Covered Software in Source Code Form, including any 163 | Modifications that You create or to which You contribute, must be under 164 | the terms of this License. You must inform recipients that the Source 165 | Code Form of the Covered Software is governed by the terms of this 166 | License, and how they can obtain a copy of this License. You may not 167 | attempt to alter or restrict the recipients' rights in the Source Code 168 | Form. 169 | 170 | 3.2. Distribution of Executable Form 171 | 172 | If You distribute Covered Software in Executable Form then: 173 | 174 | (a) such Covered Software must also be made available in Source Code 175 | Form, as described in Section 3.1, and You must inform recipients of 176 | the Executable Form how they can obtain a copy of such Source Code 177 | Form by reasonable means in a timely manner, at a charge no more 178 | than the cost of distribution to the recipient; and 179 | 180 | (b) You may distribute such Executable Form under the terms of this 181 | License, or sublicense it under different terms, provided that the 182 | license for the Executable Form does not attempt to limit or alter 183 | the recipients' rights in the Source Code Form under this License. 184 | 185 | 3.3. Distribution of a Larger Work 186 | 187 | You may create and distribute a Larger Work under terms of Your choice, 188 | provided that You also comply with the requirements of this License for 189 | the Covered Software. If the Larger Work is a combination of Covered 190 | Software with a work governed by one or more Secondary Licenses, and the 191 | Covered Software is not Incompatible With Secondary Licenses, this 192 | License permits You to additionally distribute such Covered Software 193 | under the terms of such Secondary License(s), so that the recipient of 194 | the Larger Work may, at their option, further distribute the Covered 195 | Software under the terms of either this License or such Secondary 196 | License(s). 197 | 198 | 3.4. Notices 199 | 200 | You may not remove or alter the substance of any license notices 201 | (including copyright notices, patent notices, disclaimers of warranty, 202 | or limitations of liability) contained within the Source Code Form of 203 | the Covered Software, except that You may alter any license notices to 204 | the extent required to remedy known factual inaccuracies. 205 | 206 | 3.5. Application of Additional Terms 207 | 208 | You may choose to offer, and to charge a fee for, warranty, support, 209 | indemnity or liability obligations to one or more recipients of Covered 210 | Software. However, You may do so only on Your own behalf, and not on 211 | behalf of any Contributor. You must make it absolutely clear that any 212 | such warranty, support, indemnity, or liability obligation is offered by 213 | You alone, and You hereby agree to indemnify every Contributor for any 214 | liability incurred by such Contributor as a result of warranty, support, 215 | indemnity or liability terms You offer. You may include additional 216 | disclaimers of warranty and limitations of liability specific to any 217 | jurisdiction. 218 | 219 | 4. Inability to Comply Due to Statute or Regulation 220 | --------------------------------------------------- 221 | 222 | If it is impossible for You to comply with any of the terms of this 223 | License with respect to some or all of the Covered Software due to 224 | statute, judicial order, or regulation then You must: (a) comply with 225 | the terms of this License to the maximum extent possible; and (b) 226 | describe the limitations and the code they affect. Such description must 227 | be placed in a text file included with all distributions of the Covered 228 | Software under this License. Except to the extent prohibited by statute 229 | or regulation, such description must be sufficiently detailed for a 230 | recipient of ordinary skill to be able to understand it. 231 | 232 | 5. Termination 233 | -------------- 234 | 235 | 5.1. The rights granted under this License will terminate automatically 236 | if You fail to comply with any of its terms. However, if You become 237 | compliant, then the rights granted under this License from a particular 238 | Contributor are reinstated (a) provisionally, unless and until such 239 | Contributor explicitly and finally terminates Your grants, and (b) on an 240 | ongoing basis, if such Contributor fails to notify You of the 241 | non-compliance by some reasonable means prior to 60 days after You have 242 | come back into compliance. Moreover, Your grants from a particular 243 | Contributor are reinstated on an ongoing basis if such Contributor 244 | notifies You of the non-compliance by some reasonable means, this is the 245 | first time You have received notice of non-compliance with this License 246 | from such Contributor, and You become compliant prior to 30 days after 247 | Your receipt of the notice. 248 | 249 | 5.2. If You initiate litigation against any entity by asserting a patent 250 | infringement claim (excluding declaratory judgment actions, 251 | counter-claims, and cross-claims) alleging that a Contributor Version 252 | directly or indirectly infringes any patent, then the rights granted to 253 | You by any and all Contributors for the Covered Software under Section 254 | 2.1 of this License shall terminate. 255 | 256 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all 257 | end user license agreements (excluding distributors and resellers) which 258 | have been validly granted by You or Your distributors under this License 259 | prior to termination shall survive termination. 260 | 261 | ************************************************************************ 262 | * * 263 | * 6. Disclaimer of Warranty * 264 | * ------------------------- * 265 | * * 266 | * Covered Software is provided under this License on an "as is" * 267 | * basis, without warranty of any kind, either expressed, implied, or * 268 | * statutory, including, without limitation, warranties that the * 269 | * Covered Software is free of defects, merchantable, fit for a * 270 | * particular purpose or non-infringing. The entire risk as to the * 271 | * quality and performance of the Covered Software is with You. * 272 | * Should any Covered Software prove defective in any respect, You * 273 | * (not any Contributor) assume the cost of any necessary servicing, * 274 | * repair, or correction. This disclaimer of warranty constitutes an * 275 | * essential part of this License. No use of any Covered Software is * 276 | * authorized under this License except under this disclaimer. * 277 | * * 278 | ************************************************************************ 279 | 280 | ************************************************************************ 281 | * * 282 | * 7. Limitation of Liability * 283 | * -------------------------- * 284 | * * 285 | * Under no circumstances and under no legal theory, whether tort * 286 | * (including negligence), contract, or otherwise, shall any * 287 | * Contributor, or anyone who distributes Covered Software as * 288 | * permitted above, be liable to You for any direct, indirect, * 289 | * special, incidental, or consequential damages of any character * 290 | * including, without limitation, damages for lost profits, loss of * 291 | * goodwill, work stoppage, computer failure or malfunction, or any * 292 | * and all other commercial damages or losses, even if such party * 293 | * shall have been informed of the possibility of such damages. This * 294 | * limitation of liability shall not apply to liability for death or * 295 | * personal injury resulting from such party's negligence to the * 296 | * extent applicable law prohibits such limitation. Some * 297 | * jurisdictions do not allow the exclusion or limitation of * 298 | * incidental or consequential damages, so this exclusion and * 299 | * limitation may not apply to You. * 300 | * * 301 | ************************************************************************ 302 | 303 | 8. Litigation 304 | ------------- 305 | 306 | Any litigation relating to this License may be brought only in the 307 | courts of a jurisdiction where the defendant maintains its principal 308 | place of business and such litigation shall be governed by laws of that 309 | jurisdiction, without reference to its conflict-of-law provisions. 310 | Nothing in this Section shall prevent a party's ability to bring 311 | cross-claims or counter-claims. 312 | 313 | 9. Miscellaneous 314 | ---------------- 315 | 316 | This License represents the complete agreement concerning the subject 317 | matter hereof. If any provision of this License is held to be 318 | unenforceable, such provision shall be reformed only to the extent 319 | necessary to make it enforceable. Any law or regulation which provides 320 | that the language of a contract shall be construed against the drafter 321 | shall not be used to construe this License against a Contributor. 322 | 323 | 10. Versions of the License 324 | --------------------------- 325 | 326 | 10.1. New Versions 327 | 328 | Mozilla Foundation is the license steward. Except as provided in Section 329 | 10.3, no one other than the license steward has the right to modify or 330 | publish new versions of this License. Each version will be given a 331 | distinguishing version number. 332 | 333 | 10.2. Effect of New Versions 334 | 335 | You may distribute the Covered Software under the terms of the version 336 | of the License under which You originally received the Covered Software, 337 | or under the terms of any subsequent version published by the license 338 | steward. 339 | 340 | 10.3. Modified Versions 341 | 342 | If you create software not governed by this License, and you want to 343 | create a new license for such software, you may create and use a 344 | modified version of this License if you rename the license and remove 345 | any references to the name of the license steward (except to note that 346 | such modified license differs from this License). 347 | 348 | 10.4. Distributing Source Code Form that is Incompatible With Secondary 349 | Licenses 350 | 351 | If You choose to distribute Source Code Form that is Incompatible With 352 | Secondary Licenses under the terms of this version of the License, the 353 | notice described in Exhibit B of this License must be attached. 354 | 355 | Exhibit A - Source Code Form License Notice 356 | ------------------------------------------- 357 | 358 | This Source Code Form is subject to the terms of the Mozilla Public 359 | License, v. 2.0. If a copy of the MPL was not distributed with this 360 | file, You can obtain one at http://mozilla.org/MPL/2.0/. 361 | 362 | If it is not possible or desirable to put the notice in a particular 363 | file, then You may include the notice in a location (such as a LICENSE 364 | file in a relevant directory) where a recipient would be likely to look 365 | for such a notice. 366 | 367 | You may add additional accurate notices of copyright ownership. 368 | 369 | Exhibit B - "Incompatible With Secondary Licenses" Notice 370 | --------------------------------------------------------- 371 | 372 | This Source Code Form is "Incompatible With Secondary Licenses", as 373 | defined by the Mozilla Public License, v. 2.0. 374 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | KEA_MSG_COMPILER ?= kea-msg-compiler 3 | KEA_INCLUDE ?= /usr/include/kea 4 | KEA_LIB ?= /usr/lib 5 | 6 | OBJECTS = src/messages.o src/logger.o src/load.o src/runscript.o src/callouts.o src/version.o 7 | CXXFLAGS = -I $(KEA_INCLUDE) -fPIC -Wno-deprecated -std=c++11 8 | LDFLAGS = -L $(KEA_LIB) -shared -lkea-dhcpsrv -lkea-dhcp++ -lkea-hooks -lkea-log -lkea-util -lkea-exceptions 9 | 10 | kea-hook-runscript.so: $(OBJECTS) 11 | $(CXX) -o $@ $(CXXFLAGS) $(LDFLAGS) $(OBJECTS) 12 | 13 | %.o: %.cc 14 | $(CXX) -c $(CXXFLAGS) -o $@ $< 15 | 16 | # Compile logging messages, generates messages.h and messages.cc. 17 | # Only needs to be done when changing messages.mes. 18 | # You need to build kea with `./configure --enable-generate-messages` to obtain `kea-msg-compiler` 19 | build-messages: src/messages.mes 20 | $(KEA_MSG_COMPILER) -d src/ $< 21 | 22 | clean: 23 | rm -f src/*.o 24 | rm -f kea-hook-runscript.so 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![CI status](https://code.ffdn.org/zorun/kea-hook-runscript/badges/master/pipeline.svg)](https://code.ffdn.org/zorun/kea-hook-runscript/-/pipelines) 2 | 3 | # About kea-hook-runscript 4 | 5 | This a hook for the Kea DHCP server that allows to run an external script 6 | at various points in the processing of DHCP requests and responses. 7 | 8 | The goal is to simplify integration with Kea: for many simple use-cases, 9 | it is overkill to have to write a full-blown Kea hook, where a simple shell script 10 | can do the job. 11 | 12 | This hook is licensed under the Mozilla Public License version 2 (MPL2). 13 | 14 | ## What it can and can't do 15 | 16 | Integration is mostly done one-way: thanks to this hook, Kea passes information 17 | to the external script, but the script cannot easily modify Kea's behaviour. 18 | 19 | The **external script** can be any kind of executable program, but often it will 20 | be a simple script (shell, Perl, Python...). Information about what Kea is doing 21 | is provided to the external script through environment variables: MAC address of 22 | the requesting DHCP client, IP address being handed out, etc. 23 | 24 | Each time Kea encounters a hook point, it will (by default) call the script 25 | **synchronously**. 26 | That is, Kea will do absolutely nothing else while the script is running. 27 | Thus, it is a good idea to perform only lightweight processing in the script, 28 | and absolutely avoid blocking operations. Also, scripting languages that need 29 | to initialise a huge interpreter (such as Python or Ruby) will cause a large 30 | amount of CPU usage and a massive slowdown of Kea, because the script is run 31 | multiple times for each DHCP transaction. 32 | 33 | If you know what you are doing, you can optionally call the script **asynchronously** 34 | by setting `wait` to `false` (see below). 35 | 36 | This hook works for both DHCPv4 and DHCPv6, on Kea 1.1 and above. 37 | 38 | In the future, the hook will possibly feed the return code of the external script 39 | back into Kea. This would allow the external script to cancel part of Kea's normal processing 40 | (for instance, it could be possible to easily implement a flexible host blacklist this way). 41 | 42 | ## Alternative 43 | 44 | Since Kea 1.9.5, a similar hook is provided by ISC: 45 | 46 | It has similar functionalities: information is passed to the script through environment 47 | variables. However, only **asynchronous** execution is supported in ISC's hook, at 48 | least as of Kea 1.9.5. 49 | 50 | Which hook to use is up to you: the ISC one will probably be better maintained when 51 | new versions of Kea come out, while this one supports synchronous execution which is safer. 52 | Also, environment variables are different between the two hooks, which can be an important 53 | factor if you need to process specific sub-options in your script. 54 | 55 | ## Use-cases 56 | 57 | Given the limitations exposed above, here are some example use-cases for which 58 | this hook is well-suited: 59 | 60 | - add/remove routing entries when DHCP clients arrive or leave. This can be useful 61 | when handing out IPv4 addressing in /32 subnets, or IPv6 Prefix Delegation 62 | with DHCPv6-PD. An example is included in `examples/slash32_leases/`; 63 | - update firewall rules to allow/refuse access to new DHCP clients; 64 | - log information about successful leases. 65 | 66 | For more complex use-cases, including non-trivial changes to Kea's behaviour, 67 | it may be easier to just write a Kea hook yourself. 68 | 69 | ## Examples 70 | 71 | If you have more examples of usage, feel free to contribute your Kea 72 | config and your scripts! 73 | 74 | ### Managing routes for IPv6 delegated prefixes 75 | 76 | When delegating IPv6 prefixes with DHCPv6-PD, it is necessary to add the corresponding routes 77 | in the kernel. 78 | 79 | This example script adds/removes static IPv6 routes whenever Kea delegates an IPv6 prefix 80 | through DHCPv6-PD or when the lease expires. 81 | 82 | See the included [README](examples/ipv6_prefix_delegation/README.md) for more 83 | explanations and the [source](examples/ipv6_prefix_delegation) with the script and 84 | an example Kea configuration. 85 | 86 | ### Handing out IPv4 addresses in /32 subnets 87 | 88 | This example allows to lease IPv4 addresses individually (/32 subnets), by 89 | inserting routes in the kernel each time a DHCP client connects, and 90 | sending custom routes to clients using DHCP option 121. This is mostly 91 | useful to hand out public IPv4 addresses to customers. 92 | 93 | See the included [README](examples/slash32_leases/README.md) for more 94 | explanations and the [source](examples/slash32_leases) with the script and 95 | an example Kea configuration. 96 | 97 | ### Debug script 98 | 99 | To experiment, a simple debug script is provided: `examples/debug.sh`. It 100 | simply prints the name of the hook point and all environment variables 101 | passed to it. 102 | 103 | The output of the script is at `/tmp/kea-hook-runscript-debug.log`. A nice way to debug 104 | is to continously display the content of this file: 105 | 106 | tail -F /tmp/kea-hook-runscript-debug.log 107 | 108 | ## Pre-built binaries 109 | 110 | Since version 1.4.0, we have a CI system to build the hook on various OS and for various 111 | versions of Kea. It's new, so there might be bugs. 112 | 113 | The binaries are available from the [release page](https://github.com/zorun/kea-hook-runscript/releases) 114 | or you can directly 115 | [browse through the pipeline results](https://code.ffdn.org/zorun/kea-hook-runscript/-/pipelines). 116 | 117 | ## How to build 118 | 119 | If you want to build the hook yourself, you need the Kea libraries as well 120 | as the Kea and Boost development headers. 121 | 122 | ### Using a packaged version of Kea 123 | 124 | If you use a Kea package, you need the appropriate development packages: 125 | 126 | - boost development files: `libboost-dev` or equivalent 127 | - kea development files: `isc-kea-dev` from cloudsmith (official Kea package) 128 | 129 | If you prefer using the Kea package from Debian, install `kea-dev` instead. 130 | However, it is currently unsupported and is only available in sid. 131 | 132 | Then, to build the hook, simply run: 133 | 134 | $ make -j4 135 | 136 | ### Using Kea source 137 | 138 | To build against a local Kea source tree, assumed to be in `~/kea`: 139 | 140 | - build Kea (`cd ~/kea && make -j`) 141 | - install Kea to a local directory (`cd ~/kea && make install DESTDIR=/tmp/kea`) 142 | 143 | Then build this hook with: 144 | 145 | $ export KEA_INCLUDE=$HOME/kea/src/lib 146 | $ export KEA_LIB=/tmp/kea/usr/local/lib 147 | $ make 148 | 149 | ### Supported Kea versions 150 | 151 | Some notes on Kea versions: 152 | 153 | - Kea 1.1 does not install all required headers (most notably `dhcpsrv/`), 154 | so you may need to build against Kea's source tree. 155 | - Kea 1.2 is missing a header file by mistake, so depending on your 156 | distribution, you may need to manually copy `option6_pdexclude.h` from 157 | the Kea git repository to `/usr/include/kea/dhcp/`. 158 | - Kea 1.3 to 1.7 should work out-of-the-box. 159 | - Kea 1.8 needs to run without [multi-threading](https://kea.readthedocs.io/en/kea-1.8.0/arm/dhcp4-srv.html#multi-threading-settings). 160 | Open a ticket if you need multi-threading support. 161 | 162 | ## How to use this hook 163 | 164 | If all goes well, you should obtain a `kea-hook-runscript.so` file. 165 | Then, here is how to tell Kea to use this hook, for DHCPv4: 166 | 167 | { 168 | "Dhcp4": 169 | { 170 | "hooks-libraries": [ 171 | { 172 | "library": "/path/to/hea-hook-runscript/kea-hook-runscript.so", 173 | "parameters": { 174 | "script": "/path/to/myscript.sh", 175 | "wait": true 176 | } 177 | } 178 | ], 179 | ... 180 | } 181 | } 182 | 183 | The `wait` parameter indicates whether Kea waits for the script to exit. That is, 184 | if set to `true`, Kea will block while the script is running. 185 | If you need high-performance DHCP, you can set it to `false`, but you must be prepared 186 | to handle several instances of the script running in parallel. 187 | 188 | You can use the same script for both DHCPv4 and DHCPv6, or use two different scripts. 189 | 190 | The script will receive the name of the hook point as first argument, and all 191 | relevant information available at the current hook point will be passed as 192 | environment variables, documented below. 193 | 194 | To debug, see the `examples/debug.sh` script described above. 195 | 196 | Refer to the Kea documentation for more information about each hook point: 197 | 198 | - DHCPv4 hooks reference: 199 | - DHCPv6 hooks reference: 200 | 201 | # Frequently Asked Questions 202 | 203 | ## I get "Operation not permitted" when trying to add route in my script 204 | 205 | The script is run with the same user as Kea: if this is not root, then the script will 206 | not have permission to change the routing table. 207 | 208 | Either run Kea as root, or use a passwordless sudo configuration, see 209 | [issue 24](https://github.com/zorun/kea-hook-runscript/issues/24). 210 | 211 | # Reference of variables passed to the external script 212 | 213 | ## DHCPv4 variables 214 | 215 | Here are all possible variables for DHCPv4, with their type, description 216 | and reference of the possible values. Booleans are simply expressed with 217 | `0` and `1`. 218 | 219 | | Variable name | Type | Description | Reference | 220 | |---------------------------------------|----------|-------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------| 221 | | `KEA_QUERY4_TYPE` | `string` | Type of DHCP message | [dhcp/dhcp4.h](https://jenkins.isc.org/job/Kea_doc/doxygen/d5/d8c/namespaceisc_1_1dhcp.html#aa5bfdcc4861aa3dab5328dba89362016) | 222 | | `KEA_QUERY4_INTERFACE` | `string` | Interface on which query was received | | 223 | | `KEA_QUERY4_IFINDEX` | `int` | Index of the interface on which query was received | | 224 | | `KEA_QUERY4_HWADDR` | `string` | Hardware address of the client (its MAC address) | | 225 | | `KEA_QUERY4_HWADDR_TYPE` | `int` | Type of hardware address | [dhcp/dhcp4.h](https://jenkins.isc.org/job/Kea_doc/doxygen/d5/d8c/namespaceisc_1_1dhcp.html#addcff933049489d800f9869196c8e46fa96a62c59182d6e06780b0e1ef40da059) | 226 | | `KEA_QUERY4_HWADDR_SOURCE` | `int` | How this MAC address was obtained | [dhcp/hwaddr.h](https://jenkins.isc.org/job/Kea_doc/doxygen/da/dae/group__hw__sources.html) | 227 | | `KEA_QUERY4_RELAYED` | `bool` | Whether query was relayed | [dhcp/pkt4.h](https://jenkins.isc.org/job/Kea_doc/doxygen/de/d13/classisc_1_1dhcp_1_1Pkt4.html#a8468401827b9bacdd3796bb4e20d8e5e) | 228 | | `KEA_QUERY4_RELAY_HOPS` | `int` | Number of relay agents traversed | | 229 | | `KEA_QUERY4_OPTION60` | `string` | Option 60 - vendor id | | 230 | | `KEA_QUERY4_CIADDR` | `string` | Client IP address | [dhcp/pkt4.h](https://jenkins.isc.org/job/Kea_doc/doxygen/de/d13/classisc_1_1dhcp_1_1Pkt4.html) | 231 | | `KEA_QUERY4_SIADDR` | `string` | Server IP address | [dhcp/pkt4.h](https://jenkins.isc.org/job/Kea_doc/doxygen/de/d13/classisc_1_1dhcp_1_1Pkt4.html) | 232 | | `KEA_QUERY4_YIADDR` | `string` | Your IP address | [dhcp/pkt4.h](https://jenkins.isc.org/job/Kea_doc/doxygen/de/d13/classisc_1_1dhcp_1_1Pkt4.html) | 233 | | `KEA_QUERY4_GIADDR` | `string` | Gateway IP address (inserted by DHCP relay) | [dhcp/pkt4.h](https://jenkins.isc.org/job/Kea_doc/doxygen/de/d13/classisc_1_1dhcp_1_1Pkt4.html) | 234 | | `KEA_QUERY4_RAI` | `string` | Relay Agent Information (RFC 3046) as hex string | | 235 | | `KEA_QUERY4_RAI_CIRCUIT_ID` | `string` | RAI sub-option 1 Circuit id (RFC 3046) as hex string | | 236 | | `KEA_QUERY4_RAI_REMOTE_ID` | `string` | RAI sub-option 2 Remote id (RFC 3046) as hex string | | 237 | | `KEA_RESPONSE4_TYPE` | `string` | Type of DHCP message | [dhcp/dhcp4.h](https://jenkins.isc.org/job/Kea_doc/doxygen/d5/d8c/namespaceisc_1_1dhcp.html#aa5bfdcc4861aa3dab5328dba89362016) | 238 | | `KEA_RESPONSE4_INTERFACE` | `string` | Interface on which response is being sent | | 239 | | `KEA_RESPONSE4_IFINDEX` | `int` | Index of the interface on which response is being sent | | 240 | | `KEA_RESPONSE4_HWADDR` | `string` | Hardware address of the client (its MAC address) | | 241 | | `KEA_RESPONSE4_HWADDR_TYPE` | `int` | Type of hardware address | [dhcp/dhcp4.h](https://jenkins.isc.org/job/Kea_doc/doxygen/d5/d8c/namespaceisc_1_1dhcp.html#addcff933049489d800f9869196c8e46fa96a62c59182d6e06780b0e1ef40da059) | 242 | | `KEA_RESPONSE4_HWADDR_SOURCE` | `int` | How this MAC address was obtained | [dhcp/hwaddr.h](https://jenkins.isc.org/job/Kea_doc/doxygen/da/dae/group__hw__sources.html) | 243 | | `KEA_RESPONSE4_RELAYED` | `bool` | Whether response is relayed | [dhcp/pkt4.h](https://jenkins.isc.org/job/Kea_doc/doxygen/de/d13/classisc_1_1dhcp_1_1Pkt4.html#a8468401827b9bacdd3796bb4e20d8e5e) | 244 | | `KEA_RESPONSE4_RELAY_HOPS` | `int` | Number of relay agents traversed | | 245 | | `KEA_RESPONSE4_CIADDR` | `string` | Client IP address | [dhcp/pkt4.h](https://jenkins.isc.org/job/Kea_doc/doxygen/de/d13/classisc_1_1dhcp_1_1Pkt4.html) | 246 | | `KEA_RESPONSE4_SIADDR` | `string` | Server IP address | [dhcp/pkt4.h](https://jenkins.isc.org/job/Kea_doc/doxygen/de/d13/classisc_1_1dhcp_1_1Pkt4.html) | 247 | | `KEA_RESPONSE4_YIADDR` | `string` | Your IP address | [dhcp/pkt4.h](https://jenkins.isc.org/job/Kea_doc/doxygen/de/d13/classisc_1_1dhcp_1_1Pkt4.html) | 248 | | `KEA_RESPONSE4_GIADDR` | `string` | Gateway IP address | [dhcp/pkt4.h](https://jenkins.isc.org/job/Kea_doc/doxygen/de/d13/classisc_1_1dhcp_1_1Pkt4.html) | 249 | | `KEA_SUBNET4_PREFIX` | `IPv4` | IP prefix of the subnet (without prefix length) | | 250 | | `KEA_SUBNET4_PREFIXLEN` | `int` | Prefix length of the subnet (`0` to `32`) | | 251 | | `KEA_SUBNET4` | `string` | `KEA_SUBNET4_PREFIX`/`KEA_SUBNET4_PREFIXLEN` | | 252 | | `KEA_LEASE4_ADDRESS` | `IPv4` | IPv4 address leased to client | | 253 | | `KEA_LEASE4_TYPE` | `string` | Type of lease, always equal to "V4" | | 254 | | `KEA_LEASE4_HWADDR` | `string` | Hardware address of the client | | 255 | | `KEA_LEASE4_HOSTNAME` | `string` | Hostname associated to the client | | 256 | | `KEA_LEASE4_STATE` | `string` | Current state of the lease | [dhcpsrv/lease.h](https://jenkins.isc.org/job/Kea_doc/doxygen/d0/dee/structisc_1_1dhcp_1_1Lease.html#a7075e6229e9eadedf27fc9ff49ece3c1) | 257 | | `KEA_LEASE4_IS_EXPIRED` | `bool` | Whether the lease is expired | | 258 | | `KEA_LEASE4_CLIENT_LAST_TRANSMISSION` | `int` | Unix timestamp of the last message received from the client | [dhcpsrv/lease.h](https://jenkins.isc.org/job/Kea_doc/doxygen/d0/dee/structisc_1_1dhcp_1_1Lease.html#ac71dc7f97dd753096a0f448c6649cdcf) | 259 | | `KEA_LEASE4_VALID_LIFETIME` | `int` | Valid lifetime of the lease, in seconds | [dhcpsrv/lease.h](https://jenkins.isc.org/job/Kea_doc/doxygen/d0/dee/structisc_1_1dhcp_1_1Lease.html#a615302a9140991942225b9809ddd50fb) | 260 | | `KEA_REMOVE_LEASE` | `bool` | Whether the lease should be removed from the lease database | [DHCPv4 hook API](https://jenkins.isc.org/job/Kea_doc/doxygen/de/df3/dhcpv4Hooks.html#dhcpv4HooksLease4Expire) | 261 | | `KEA_FAKE_ALLOCATION` | `bool` | Whether the query is a DISCOVER or a REQUEST | [DHCPv4 hook API](https://jenkins.isc.org/job/Kea_doc/doxygen/de/df3/dhcpv4Hooks.html#dhcpv4HooksLeaseSelect) | 262 | 263 | ## DHCPv4 hook points 264 | 265 | For each Kea hook point, here are all variables usable in the external 266 | script. 267 | 268 | ### [`pkt4_receive`](https://jenkins.isc.org/job/Kea_doc/doxygen/de/df3/dhcpv4Hooks.html#dhcpv4HooksPkt4Receive) 269 | 270 | - `KEA_QUERY4_TYPE` 271 | - `KEA_QUERY4_INTERFACE` 272 | - `KEA_QUERY4_IFINDEX` 273 | - `KEA_QUERY4_HWADDR` 274 | - `KEA_QUERY4_HWADDR_SOURCE` 275 | - `KEA_QUERY4_HWADDR_TYPE` 276 | - `KEA_QUERY4_RELAYED` 277 | - `KEA_QUERY4_RELAY_HOPS` 278 | - `KEA_QUERY4_OPTION60` 279 | - `KEA_QUERY4_CIADDR` 280 | - `KEA_QUERY4_SIADDR` 281 | - `KEA_QUERY4_YIADDR` 282 | - `KEA_QUERY4_GIADDR` 283 | - `KEA_QUERY4_RAI` 284 | - `KEA_QUERY4_RAI_CIRCUIT_ID` 285 | - `KEA_QUERY4_RAI_REMOTE_ID` 286 | 287 | ### [`pkt4_send`](https://jenkins.isc.org/job/Kea_doc/doxygen/de/df3/dhcpv4Hooks.html#dhcpv4HooksPkt4Send) 288 | 289 | - `KEA_QUERY4_TYPE` 290 | - `KEA_QUERY4_INTERFACE` 291 | - `KEA_QUERY4_IFINDEX` 292 | - `KEA_QUERY4_HWADDR` 293 | - `KEA_QUERY4_HWADDR_SOURCE` 294 | - `KEA_QUERY4_HWADDR_TYPE` 295 | - `KEA_QUERY4_RELAYED` 296 | - `KEA_QUERY4_RELAY_HOPS` 297 | - `KEA_QUERY4_OPTION60` 298 | - `KEA_QUERY4_CIADDR` 299 | - `KEA_QUERY4_SIADDR` 300 | - `KEA_QUERY4_YIADDR` 301 | - `KEA_QUERY4_GIADDR` 302 | - `KEA_QUERY4_RAI` 303 | - `KEA_QUERY4_RAI_CIRCUIT_ID` 304 | - `KEA_QUERY4_RAI_REMOTE_ID` 305 | - `KEA_RESPONSE4_TYPE` 306 | - `KEA_RESPONSE4_INTERFACE` 307 | - `KEA_RESPONSE4_IFINDEX` 308 | - `KEA_RESPONSE4_HWADDR` 309 | - `KEA_RESPONSE4_HWADDR_SOURCE` 310 | - `KEA_RESPONSE4_HWADDR_TYPE` 311 | - `KEA_RESPONSE4_RELAYED` 312 | - `KEA_RESPONSE4_RELAY_HOPS` 313 | - `KEA_RESPONSE4_CIADDR` 314 | - `KEA_RESPONSE4_SIADDR` 315 | - `KEA_RESPONSE4_YIADDR` 316 | - `KEA_RESPONSE4_GIADDR` 317 | 318 | ### [`subnet4_select`](https://jenkins.isc.org/job/Kea_doc/doxygen/de/df3/dhcpv4Hooks.html#dhcpv4HooksSubnet4Select) 319 | 320 | - `KEA_QUERY4_TYPE` 321 | - `KEA_QUERY4_INTERFACE` 322 | - `KEA_QUERY4_IFINDEX` 323 | - `KEA_QUERY4_HWADDR` 324 | - `KEA_QUERY4_HWADDR_SOURCE` 325 | - `KEA_QUERY4_HWADDR_TYPE` 326 | - `KEA_QUERY4_RELAYED` 327 | - `KEA_QUERY4_RELAY_HOPS` 328 | - `KEA_QUERY4_CIADDR` 329 | - `KEA_QUERY4_SIADDR` 330 | - `KEA_QUERY4_YIADDR` 331 | - `KEA_QUERY4_GIADDR` 332 | - `KEA_QUERY4_RAI` 333 | - `KEA_QUERY4_RAI_CIRCUIT_ID` 334 | - `KEA_QUERY4_RAI_REMOTE_ID` 335 | - `KEA_SUBNET4_PREFIX` 336 | - `KEA_SUBNET4_PREFIXLEN` 337 | - `KEA_SUBNET4` 338 | 339 | ### [`lease4_select`](https://jenkins.isc.org/job/Kea_doc/doxygen/de/df3/dhcpv4Hooks.html#dhcpv4HooksLease4Select) 340 | 341 | - `KEA_QUERY4_TYPE` 342 | - `KEA_QUERY4_INTERFACE` 343 | - `KEA_QUERY4_IFINDEX` 344 | - `KEA_QUERY4_HWADDR` 345 | - `KEA_QUERY4_HWADDR_SOURCE` 346 | - `KEA_QUERY4_HWADDR_TYPE` 347 | - `KEA_QUERY4_RELAYED` 348 | - `KEA_QUERY4_RELAY_HOPS` 349 | - `KEA_QUERY4_CIADDR` 350 | - `KEA_QUERY4_SIADDR` 351 | - `KEA_QUERY4_YIADDR` 352 | - `KEA_QUERY4_GIADDR` 353 | - `KEA_QUERY4_RAI` 354 | - `KEA_QUERY4_RAI_CIRCUIT_ID` 355 | - `KEA_QUERY4_RAI_REMOTE_ID` 356 | - `KEA_SUBNET4_PREFIX` 357 | - `KEA_SUBNET4_PREFIXLEN` 358 | - `KEA_SUBNET4` 359 | - `KEA_FAKE_ALLOCATION` 360 | - `KEA_LEASE4_ADDRESS` 361 | - `KEA_LEASE4_TYPE` 362 | - `KEA_LEASE4_STATE` 363 | - `KEA_LEASE4_IS_EXPIRED` 364 | - `KEA_LEASE4_HWADDR` 365 | - `KEA_LEASE4_HOSTNAME` 366 | - `KEA_LEASE4_CLIENT_LAST_TRANSMISSION` 367 | - `KEA_LEASE4_VALID_LIFETIME` 368 | 369 | ### [`lease4_renew`](https://jenkins.isc.org/job/Kea_doc/doxygen/de/df3/dhcpv4Hooks.html#dhcpv4HooksLease4Renew) 370 | 371 | - `KEA_QUERY4_TYPE` 372 | - `KEA_QUERY4_INTERFACE` 373 | - `KEA_QUERY4_IFINDEX` 374 | - `KEA_QUERY4_HWADDR` 375 | - `KEA_QUERY4_HWADDR_SOURCE` 376 | - `KEA_QUERY4_HWADDR_TYPE` 377 | - `KEA_QUERY4_RELAYED` 378 | - `KEA_QUERY4_RELAY_HOPS` 379 | - `KEA_QUERY4_CIADDR` 380 | - `KEA_QUERY4_SIADDR` 381 | - `KEA_QUERY4_YIADDR` 382 | - `KEA_QUERY4_GIADDR` 383 | - `KEA_QUERY4_RAI` 384 | - `KEA_QUERY4_RAI_CIRCUIT_ID` 385 | - `KEA_QUERY4_RAI_REMOTE_ID` 386 | - `KEA_SUBNET4_PREFIX` 387 | - `KEA_SUBNET4_PREFIXLEN` 388 | - `KEA_SUBNET4` 389 | - `KEA_LEASE4_ADDRESS` 390 | - `KEA_LEASE4_TYPE` 391 | - `KEA_LEASE4_STATE` 392 | - `KEA_LEASE4_IS_EXPIRED` 393 | - `KEA_LEASE4_HWADDR` 394 | - `KEA_LEASE4_HOSTNAME` 395 | - `KEA_LEASE4_CLIENT_LAST_TRANSMISSION` 396 | - `KEA_LEASE4_VALID_LIFETIME` 397 | 398 | ### [`lease4_release`](https://jenkins.isc.org/job/Kea_doc/doxygen/de/df3/dhcpv4Hooks.html#dhcpv4HooksLease4Release) 399 | 400 | - `KEA_QUERY4_TYPE` 401 | - `KEA_QUERY4_INTERFACE` 402 | - `KEA_QUERY4_IFINDEX` 403 | - `KEA_QUERY4_HWADDR` 404 | - `KEA_QUERY4_HWADDR_SOURCE` 405 | - `KEA_QUERY4_HWADDR_TYPE` 406 | - `KEA_QUERY4_RELAYED` 407 | - `KEA_QUERY4_RELAY_HOPS` 408 | - `KEA_QUERY4_CIADDR` 409 | - `KEA_QUERY4_SIADDR` 410 | - `KEA_QUERY4_YIADDR` 411 | - `KEA_QUERY4_GIADDR` 412 | - `KEA_QUERY4_RAI` 413 | - `KEA_QUERY4_RAI_CIRCUIT_ID` 414 | - `KEA_QUERY4_RAI_REMOTE_ID` 415 | - `KEA_LEASE4_ADDRESS` 416 | - `KEA_LEASE4_TYPE` 417 | - `KEA_LEASE4_STATE` 418 | - `KEA_LEASE4_IS_EXPIRED` 419 | - `KEA_LEASE4_HWADDR` 420 | - `KEA_LEASE4_HOSTNAME` 421 | - `KEA_LEASE4_CLIENT_LAST_TRANSMISSION` 422 | - `KEA_LEASE4_VALID_LIFETIME` 423 | 424 | ### [`lease4_decline`](https://jenkins.isc.org/job/Kea_doc/doxygen/de/df3/dhcpv4Hooks.html#dhcpv4HooksLease4Decline) 425 | 426 | - `KEA_QUERY4_TYPE` 427 | - `KEA_QUERY4_INTERFACE` 428 | - `KEA_QUERY4_IFINDEX` 429 | - `KEA_QUERY4_HWADDR` 430 | - `KEA_QUERY4_HWADDR_SOURCE` 431 | - `KEA_QUERY4_HWADDR_TYPE` 432 | - `KEA_QUERY4_RELAYED` 433 | - `KEA_QUERY4_RELAY_HOPS` 434 | - `KEA_QUERY4_CIADDR` 435 | - `KEA_QUERY4_SIADDR` 436 | - `KEA_QUERY4_YIADDR` 437 | - `KEA_QUERY4_GIADDR` 438 | - `KEA_QUERY4_RAI` 439 | - `KEA_QUERY4_RAI_CIRCUIT_ID` 440 | - `KEA_QUERY4_RAI_REMOTE_ID` 441 | - `KEA_LEASE4_ADDRESS` 442 | - `KEA_LEASE4_TYPE` 443 | - `KEA_LEASE4_STATE` 444 | - `KEA_LEASE4_IS_EXPIRED` 445 | - `KEA_LEASE4_HWADDR` 446 | - `KEA_LEASE4_HOSTNAME` 447 | - `KEA_LEASE4_CLIENT_LAST_TRANSMISSION` 448 | - `KEA_LEASE4_VALID_LIFETIME` 449 | 450 | ### [`lease4_expire`](https://jenkins.isc.org/job/Kea_doc/doxygen/de/df3/dhcpv4Hooks.html#dhcpv4HooksLease4Expire) 451 | 452 | - `KEA_LEASE4_ADDRESS` 453 | - `KEA_LEASE4_TYPE` 454 | - `KEA_LEASE4_STATE` 455 | - `KEA_LEASE4_IS_EXPIRED` 456 | - `KEA_LEASE4_HWADDR` 457 | - `KEA_LEASE4_HOSTNAME` 458 | - `KEA_LEASE4_CLIENT_LAST_TRANSMISSION` 459 | - `KEA_LEASE4_VALID_LIFETIME` 460 | - `KEA_REMOVE_LEASE` 461 | 462 | ### [`lease4_recover`](https://jenkins.isc.org/job/Kea_doc/doxygen/de/df3/dhcpv4Hooks.html#dhcpv4HooksLease4Recover) 463 | 464 | - `KEA_LEASE4_ADDRESS` 465 | - `KEA_LEASE4_TYPE` 466 | - `KEA_LEASE4_STATE` 467 | - `KEA_LEASE4_IS_EXPIRED` 468 | - `KEA_LEASE4_HWADDR` 469 | - `KEA_LEASE4_HOSTNAME` 470 | - `KEA_LEASE4_CLIENT_LAST_TRANSMISSION` 471 | - `KEA_LEASE4_VALID_LIFETIME` 472 | 473 | 474 | ## DHCPv6 variables 475 | 476 | Here are all possible variables for DHCPv6, with their type, description 477 | and reference of the possible values. Booleans are simply expressed with 478 | `0` and `1`. 479 | 480 | | Variable name | Type | Description | Reference | 481 | |---------------------------------------|----------|-------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------| 482 | | `KEA_QUERY6_TYPE` | `string` | Type of DHCPv6 message | [dhcp/dhcp6.h](https://jenkins.isc.org/job/Kea_doc/doxygen/db/d87/dhcp6_8h_source.html) | 483 | | `KEA_QUERY6_INTERFACE` | `string` | Interface on which query was received | | 484 | | `KEA_QUERY6_IFINDEX` | `int` | Index of the interface on which query was received | | 485 | | `KEA_QUERY6_DUID` | `string` | TODO | | 486 | | `KEA_QUERY6_HWADDR` | `string` | Hardware address of the client (its MAC address) | | 487 | | `KEA_QUERY6_HWADDR_TYPE` | `int` | Type of hardware address | [dhcp/dhcp4.h](https://jenkins.isc.org/job/Kea_doc/doxygen/d5/d8c/namespaceisc_1_1dhcp.html#addcff933049489d800f9869196c8e46f) | 488 | | `KEA_QUERY6_HWADDR_SOURCE` | `int` | How this MAC address was obtained | [dhcp/hwaddr.h](https://jenkins.isc.org/job/Kea_doc/doxygen/da/dae/group__hw__sources.html) | 489 | | `KEA_QUERY6_LOCAL_ADDRESS` | `string` | Local IPv6 address on which the query was received (link-local or multicast) | [dhcp/pkt.h](https://jenkins.isc.org/job/Kea_doc/doxygen/de/d71/classisc_1_1dhcp_1_1Pkt.html#a55b5c3f4cbab0f60968b0498d8543c65) | 490 | | `KEA_QUERY6_LOCAL_PORT` | `int` | Local UDP or TCP port | | 491 | | `KEA_QUERY6_REMOTE_ADDRESS` | `string` | Remote IPv6 address, from which the query was received (link-local) | [dhcp/pkt.h](https://jenkins.isc.org/job/Kea_doc/doxygen/de/d71/classisc_1_1dhcp_1_1Pkt.html#a1e20bcdc69d5f97ed8cc48290017b8d9) | 492 | | `KEA_QUERY6_REMOTE_PORT` | `int` | Remote UDP or TCP port | | 493 | | `KEA_QUERY6_LABEL` | `string` | Unique identifier of the query, to be used e.g. in log messages | [dhcp/pkt.h](https://jenkins.isc.org/job/Kea_doc/doxygen/de/d71/classisc_1_1dhcp_1_1Pkt.html#af9888e61c5304f4bac1983a93ac6a473) | 494 | | `KEA_QUERY6_TRANSACTION_ID` | `int` | Transaction ID of the query | [dhcp/pkt.h](https://jenkins.isc.org/job/Kea_doc/doxygen/de/d71/classisc_1_1dhcp_1_1Pkt.html#a8cd6c6ab6c434b1bf6949bb1cc4102b1) | 495 | | `KEA_RESPONSE6_TYPE` | `string` | Type of DHCPv6 message | [dhcp/dhcp6.h](https://jenkins.isc.org/job/Kea_doc/doxygen/db/d87/dhcp6_8h_source.html) | 496 | | `KEA_RESPONSE6_INTERFACE` | `string` | Interface on which response is being sent | | 497 | | `KEA_RESPONSE6_IFINDEX` | `int` | Index of the interface on which response is being sent | | 498 | | `KEA_RESPONSE6_DUID` | `string` | TODO | | 499 | | `KEA_RESPONSE6_HWADDR` | `string` | Hardware address of the client (its MAC address) | | 500 | | `KEA_RESPONSE6_HWADDR_TYPE` | `int` | Type of hardware address | [dhcp/dhcp4.h](https://jenkins.isc.org/job/Kea_doc/doxygen/d5/d8c/namespaceisc_1_1dhcp.html#addcff933049489d800f9869196c8e46f) | 501 | | `KEA_RESPONSE6_HWADDR_SOURCE` | `int` | How this MAC address was obtained | [dhcp/hwaddr.h](https://jenkins.isc.org/job/Kea_doc/doxygen/da/dae/group__hw__sources.html) | 502 | | `KEA_RESPONSE6_LOCAL_ADDRESS` | `string` | Local IPv6 address, from which the response is being sent (link-local or multicast) | [dhcp/pkt.h](https://jenkins.isc.org/job/Kea_doc/doxygen/de/d71/classisc_1_1dhcp_1_1Pkt.html#a55b5c3f4cbab0f60968b0498d8543c65) | 503 | | `KEA_RESPONSE6_LOCAL_PORT` | `int` | Local UDP or TCP port | | 504 | | `KEA_RESPONSE6_REMOTE_ADDRESS` | `string` | Remote IPv6 address, to which the response is being sent (link-local) | [dhcp/pkt.h](https://jenkins.isc.org/job/Kea_doc/doxygen/de/d71/classisc_1_1dhcp_1_1Pkt.html#a1e20bcdc69d5f97ed8cc48290017b8d9) | 505 | | `KEA_RESPONSE6_REMOTE_PORT` | `int` | Remote UDP or TCP port | | 506 | | `KEA_RESPONSE6_LABEL` | `string` | Unique identifier of the response, to be used e.g. in log messages | [dhcp/pkt.h](https://jenkins.isc.org/job/Kea_doc/doxygen/de/d71/classisc_1_1dhcp_1_1Pkt.html#af9888e61c5304f4bac1983a93ac6a473) | 507 | | `KEA_RESPONSE6_TRANSACTION_ID` | `int` | Transaction ID of the response | [dhcp/pkt.h](https://jenkins.isc.org/job/Kea_doc/doxygen/de/d71/classisc_1_1dhcp_1_1Pkt.html#a8cd6c6ab6c434b1bf6949bb1cc4102b1) | 508 | | `KEA_SUBNET6_PREFIX` | `IPv6` | IP prefix of the subnet (without prefix length) | | 509 | | `KEA_SUBNET6_PREFIXLEN` | `int` | Prefix length of the subnet (`0` to `128`) | | 510 | | `KEA_SUBNET6` | `string` | `KEA_SUBNET6_PREFIX`/`KEA_SUBNET6_PREFIXLEN` | | 511 | | `KEA_LEASE6_TYPE` | `string` | Type of lease, either "NA", "TA", or "PD" | [dhcp/lease.h](https://jenkins.isc.org/job/Kea_doc/doxygen/d0/dee/structisc_1_1dhcp_1_1Lease.html#a9257a6a410119ea79f29b9d2756c8769) | 512 | | `KEA_LEASE6_ADDRESS` | `IPv6` | IPv6 address leased to client | | 513 | | `KEA_LEASE6_DELEGATED_PREFIX` | `string` | For TYPE="PD", prefix delegated to client (in `prefix/prefixlen` form) | | 514 | | `KEA_LEASE6_DELEGATED_PREFIXLEN` | `int` | For TYPE="PD", length of the prefix delegated to client | | 515 | | `KEA_LEASE6_CLIENT_DUID` | `string` | DUID of the client | | 516 | | `KEA_LEASE6_HWADDR` | `string` | Hardware address of the client | | 517 | | `KEA_LEASE6_HOSTNAME` | `string` | Hostname associated to the client | | 518 | | `KEA_LEASE6_STATE` | `string` | Current state of the lease | [dhcpsrv/lease.h](https://jenkins.isc.org/job/Kea_doc/doxygen/d0/dee/structisc_1_1dhcp_1_1Lease.html#a7075e6229e9eadedf27fc9ff49ece3c1) | 519 | | `KEA_LEASE6_IS_EXPIRED` | `bool` | Whether the lease is expired | | 520 | | `KEA_LEASE6_CLIENT_LAST_TRANSMISSION` | `int` | Unix timestamp of the last message received from the client | [dhcpsrv/lease.h](https://jenkins.isc.org/job/Kea_doc/doxygen/d0/dee/structisc_1_1dhcp_1_1Lease.html#ac71dc7f97dd753096a0f448c6649cdcf) | 521 | | `KEA_LEASE6_VALID_LIFETIME` | `int` | Valid lifetime of the lease, in seconds | [dhcpsrv/lease.h](https://jenkins.isc.org/job/Kea_doc/doxygen/d0/dee/structisc_1_1dhcp_1_1Lease.html#a615302a9140991942225b9809ddd50fb) | 522 | | `KEA_LEASE6_PREFERRED_LIFETIME` | `int` | Preferred lifetime of the lease, in seconds | [dhcpsrv/lease.h](https://jenkins.isc.org/job/Kea_doc/doxygen/da/ddc/structisc_1_1dhcp_1_1Lease6.html#acece7ab17d67a657637cf16a9a2f1f6e) | 523 | | `KEA_LEASE6_IAID` | `string` | Identity Association Identifier, to differentiate between IA containers | [dhcpsrv/lease.h](https://jenkins.isc.org/job/Kea_doc/doxygen/da/ddc/structisc_1_1dhcp_1_1Lease6.html#acc2e175c33e09dbdc8c93b943488431e) | 524 | | `KEA_REMOVE_LEASE` | `bool` | Whether the lease should be removed from the lease database | [DHCPv6 hook API](https://jenkins.isc.org/job/Kea_doc/doxygen/d1/d02/dhcpv6Hooks.html#dhcpv6HooksLease6Expire) | 525 | | `KEA_FAKE_ALLOCATION` | `bool` | Whether the query is a SOLICIT or a REQUEST | [DHCPv6 hook API](https://jenkins.isc.org/job/Kea_doc/doxygen/d1/d02/dhcpv6Hooks.html#dhcpv6HooksLease6Select) | 526 | 527 | ## DHCPv6 hook points 528 | 529 | For each Kea hook point, here are all variables usable in the external 530 | script. 531 | 532 | ### [`pkt6_receive`](https://jenkins.isc.org/job/Kea_doc/doxygen/d1/d02/dhcpv6Hooks.html#dhcpv6HooksPkt6Receive) 533 | 534 | - `KEA_QUERY6_TYPE` 535 | - `KEA_QUERY6_INTERFACE` 536 | - `KEA_QUERY6_IFINDEX` 537 | - `KEA_QUERY6_DUID` 538 | - `KEA_QUERY6_HWADDR` 539 | - `KEA_QUERY6_HWADDR_TYPE` 540 | - `KEA_QUERY6_HWADDR_SOURCE` 541 | - `KEA_QUERY6_LOCAL_ADDRESS` 542 | - `KEA_QUERY6_LOCAL_PORT` 543 | - `KEA_QUERY6_REMOTE_ADDRESS` 544 | - `KEA_QUERY6_REMOTE_PORT` 545 | - `KEA_QUERY6_LABEL` 546 | - `KEA_QUERY6_TRANSACTION_ID` 547 | 548 | ### [`pkt6_send`](https://jenkins.isc.org/job/Kea_doc/doxygen/d1/d02/dhcpv6Hooks.html#dhcpv6HooksPkt6Send) 549 | 550 | - `KEA_QUERY6_TYPE` 551 | - `KEA_QUERY6_INTERFACE` 552 | - `KEA_QUERY6_IFINDEX` 553 | - `KEA_QUERY6_DUID` 554 | - `KEA_QUERY6_HWADDR` 555 | - `KEA_QUERY6_HWADDR_TYPE` 556 | - `KEA_QUERY6_HWADDR_SOURCE` 557 | - `KEA_QUERY6_LOCAL_ADDRESS` 558 | - `KEA_QUERY6_LOCAL_PORT` 559 | - `KEA_QUERY6_REMOTE_ADDRESS` 560 | - `KEA_QUERY6_REMOTE_PORT` 561 | - `KEA_QUERY6_LABEL` 562 | - `KEA_QUERY6_TRANSACTION_ID` 563 | - `KEA_RESPONSE6_TYPE` 564 | - `KEA_RESPONSE6_INTERFACE` 565 | - `KEA_RESPONSE6_IFINDEX` 566 | - `KEA_RESPONSE6_DUID` 567 | - `KEA_RESPONSE6_HWADDR` 568 | - `KEA_RESPONSE6_HWADDR_TYPE` 569 | - `KEA_RESPONSE6_HWADDR_SOURCE` 570 | - `KEA_RESPONSE6_LOCAL_ADDRESS` 571 | - `KEA_RESPONSE6_LOCAL_PORT` 572 | - `KEA_RESPONSE6_REMOTE_ADDRESS` 573 | - `KEA_RESPONSE6_REMOTE_PORT` 574 | - `KEA_RESPONSE6_LABEL` 575 | - `KEA_RESPONSE6_TRANSACTION_ID` 576 | 577 | ### [`subnet6_select`](https://jenkins.isc.org/job/Kea_doc/doxygen/d1/d02/dhcpv6Hooks.html#dhcpv6HooksSubnet6Select) 578 | 579 | - `KEA_QUERY6_TYPE` 580 | - `KEA_QUERY6_INTERFACE` 581 | - `KEA_QUERY6_IFINDEX` 582 | - `KEA_QUERY6_DUID` 583 | - `KEA_QUERY6_HWADDR` 584 | - `KEA_QUERY6_HWADDR_TYPE` 585 | - `KEA_QUERY6_HWADDR_SOURCE` 586 | - `KEA_QUERY6_LOCAL_ADDRESS` 587 | - `KEA_QUERY6_LOCAL_PORT` 588 | - `KEA_QUERY6_REMOTE_ADDRESS` 589 | - `KEA_QUERY6_REMOTE_PORT` 590 | - `KEA_QUERY6_LABEL` 591 | - `KEA_QUERY6_TRANSACTION_ID` 592 | - `KEA_SUBNET6_PREFIX` 593 | - `KEA_SUBNET6_PREFIXLEN` 594 | - `KEA_SUBNET6` 595 | 596 | ### [`lease6_select`](https://jenkins.isc.org/job/Kea_doc/doxygen/d1/d02/dhcpv6Hooks.html#dhcpv6HooksLease6Select) 597 | 598 | - `KEA_QUERY6_TYPE` 599 | - `KEA_QUERY6_INTERFACE` 600 | - `KEA_QUERY6_IFINDEX` 601 | - `KEA_QUERY6_DUID` 602 | - `KEA_QUERY6_HWADDR` 603 | - `KEA_QUERY6_HWADDR_TYPE` 604 | - `KEA_QUERY6_HWADDR_SOURCE` 605 | - `KEA_QUERY6_LOCAL_ADDRESS` 606 | - `KEA_QUERY6_LOCAL_PORT` 607 | - `KEA_QUERY6_REMOTE_ADDRESS` 608 | - `KEA_QUERY6_REMOTE_PORT` 609 | - `KEA_QUERY6_LABEL` 610 | - `KEA_QUERY6_TRANSACTION_ID` 611 | - `KEA_SUBNET6_PREFIX` 612 | - `KEA_SUBNET6_PREFIXLEN` 613 | - `KEA_SUBNET6` 614 | - `KEA_FAKE_ALLOCATION` 615 | - `KEA_LEASE6_TYPE` 616 | - `KEA_LEASE6_ADDRESS` 617 | - `KEA_LEASE6_DELEGATED_PREFIX` 618 | - `KEA_LEASE6_DELEGATED_PREFIXLEN` 619 | - `KEA_LEASE6_CLIENT_DUID` 620 | - `KEA_LEASE6_HWADDR` 621 | - `KEA_LEASE6_HOSTNAME` 622 | - `KEA_LEASE6_STATE` 623 | - `KEA_LEASE6_IS_EXPIRED` 624 | - `KEA_LEASE6_CLIENT_LAST_TRANSMISSION` 625 | - `KEA_LEASE6_VALID_LIFETIME` 626 | - `KEA_LEASE6_PREFERRED_LIFETIME` 627 | - `KEA_LEASE6_IAID` 628 | 629 | ### [`lease6_renew`](https://jenkins.isc.org/job/Kea_doc/doxygen/d1/d02/dhcpv6Hooks.html#dhcpv6HooksLease6Renew) 630 | 631 | - `KEA_QUERY6_TYPE` 632 | - `KEA_QUERY6_INTERFACE` 633 | - `KEA_QUERY6_IFINDEX` 634 | - `KEA_QUERY6_DUID` 635 | - `KEA_QUERY6_HWADDR` 636 | - `KEA_QUERY6_HWADDR_TYPE` 637 | - `KEA_QUERY6_HWADDR_SOURCE` 638 | - `KEA_QUERY6_LOCAL_ADDRESS` 639 | - `KEA_QUERY6_LOCAL_PORT` 640 | - `KEA_QUERY6_REMOTE_ADDRESS` 641 | - `KEA_QUERY6_REMOTE_PORT` 642 | - `KEA_QUERY6_LABEL` 643 | - `KEA_QUERY6_TRANSACTION_ID` 644 | - `KEA_LEASE6_TYPE` 645 | - `KEA_LEASE6_ADDRESS` 646 | - `KEA_LEASE6_DELEGATED_PREFIX` 647 | - `KEA_LEASE6_DELEGATED_PREFIXLEN` 648 | - `KEA_LEASE6_CLIENT_DUID` 649 | - `KEA_LEASE6_HWADDR` 650 | - `KEA_LEASE6_HOSTNAME` 651 | - `KEA_LEASE6_STATE` 652 | - `KEA_LEASE6_IS_EXPIRED` 653 | - `KEA_LEASE6_CLIENT_LAST_TRANSMISSION` 654 | - `KEA_LEASE6_VALID_LIFETIME` 655 | - `KEA_LEASE6_PREFERRED_LIFETIME` 656 | - `KEA_LEASE6_IAID` 657 | 658 | ### [`lease6_rebind`](https://jenkins.isc.org/job/Kea_doc/doxygen/d1/d02/dhcpv6Hooks.html#dhcpv6HooksLease6Rebind) 659 | 660 | - `KEA_QUERY6_TYPE` 661 | - `KEA_QUERY6_INTERFACE` 662 | - `KEA_QUERY6_IFINDEX` 663 | - `KEA_QUERY6_DUID` 664 | - `KEA_QUERY6_HWADDR` 665 | - `KEA_QUERY6_HWADDR_TYPE` 666 | - `KEA_QUERY6_HWADDR_SOURCE` 667 | - `KEA_QUERY6_LOCAL_ADDRESS` 668 | - `KEA_QUERY6_LOCAL_PORT` 669 | - `KEA_QUERY6_REMOTE_ADDRESS` 670 | - `KEA_QUERY6_REMOTE_PORT` 671 | - `KEA_QUERY6_LABEL` 672 | - `KEA_QUERY6_TRANSACTION_ID` 673 | - `KEA_LEASE6_TYPE` 674 | - `KEA_LEASE6_ADDRESS` 675 | - `KEA_LEASE6_DELEGATED_PREFIX` 676 | - `KEA_LEASE6_DELEGATED_PREFIXLEN` 677 | - `KEA_LEASE6_CLIENT_DUID` 678 | - `KEA_LEASE6_HWADDR` 679 | - `KEA_LEASE6_HOSTNAME` 680 | - `KEA_LEASE6_STATE` 681 | - `KEA_LEASE6_IS_EXPIRED` 682 | - `KEA_LEASE6_CLIENT_LAST_TRANSMISSION` 683 | - `KEA_LEASE6_VALID_LIFETIME` 684 | - `KEA_LEASE6_PREFERRED_LIFETIME` 685 | - `KEA_LEASE6_IAID` 686 | 687 | ### [`lease6_decline`](https://jenkins.isc.org/job/Kea_doc/doxygen/d1/d02/dhcpv6Hooks.html#dhcpv6HooksLease6Decline) 688 | 689 | - `KEA_QUERY6_TYPE` 690 | - `KEA_QUERY6_INTERFACE` 691 | - `KEA_QUERY6_DUID` 692 | - `KEA_QUERY6_HWADDR` 693 | - `KEA_QUERY6_HWADDR_TYPE` 694 | - `KEA_QUERY6_HWADDR_SOURCE` 695 | - `KEA_QUERY6_LOCAL_ADDRESS` 696 | - `KEA_QUERY6_LOCAL_PORT` 697 | - `KEA_QUERY6_REMOTE_ADDRESS` 698 | - `KEA_QUERY6_REMOTE_PORT` 699 | - `KEA_QUERY6_LABEL` 700 | - `KEA_QUERY6_TRANSACTION_ID` 701 | - `KEA_LEASE6_TYPE` 702 | - `KEA_LEASE6_ADDRESS` 703 | - `KEA_LEASE6_DELEGATED_PREFIX` 704 | - `KEA_LEASE6_DELEGATED_PREFIXLEN` 705 | - `KEA_LEASE6_CLIENT_DUID` 706 | - `KEA_LEASE6_HWADDR` 707 | - `KEA_LEASE6_HOSTNAME` 708 | - `KEA_LEASE6_STATE` 709 | - `KEA_LEASE6_IS_EXPIRED` 710 | - `KEA_LEASE6_CLIENT_LAST_TRANSMISSION` 711 | - `KEA_LEASE6_VALID_LIFETIME` 712 | - `KEA_LEASE6_PREFERRED_LIFETIME` 713 | - `KEA_LEASE6_IAID` 714 | 715 | ### [`lease6_release`](https://jenkins.isc.org/job/Kea_doc/doxygen/d1/d02/dhcpv6Hooks.html#dhcpv6HooksLease6Release) 716 | 717 | - `KEA_QUERY6_TYPE` 718 | - `KEA_QUERY6_INTERFACE` 719 | - `KEA_QUERY6_IFINDEX` 720 | - `KEA_QUERY6_DUID` 721 | - `KEA_QUERY6_HWADDR` 722 | - `KEA_QUERY6_HWADDR_TYPE` 723 | - `KEA_QUERY6_HWADDR_SOURCE` 724 | - `KEA_QUERY6_LOCAL_ADDRESS` 725 | - `KEA_QUERY6_LOCAL_PORT` 726 | - `KEA_QUERY6_REMOTE_ADDRESS` 727 | - `KEA_QUERY6_REMOTE_PORT` 728 | - `KEA_QUERY6_LABEL` 729 | - `KEA_QUERY6_TRANSACTION_ID` 730 | - `KEA_LEASE6_TYPE` 731 | - `KEA_LEASE6_ADDRESS` 732 | - `KEA_LEASE6_DELEGATED_PREFIX` 733 | - `KEA_LEASE6_DELEGATED_PREFIXLEN` 734 | - `KEA_LEASE6_CLIENT_DUID` 735 | - `KEA_LEASE6_HWADDR` 736 | - `KEA_LEASE6_HOSTNAME` 737 | - `KEA_LEASE6_STATE` 738 | - `KEA_LEASE6_IS_EXPIRED` 739 | - `KEA_LEASE6_CLIENT_LAST_TRANSMISSION` 740 | - `KEA_LEASE6_VALID_LIFETIME` 741 | - `KEA_LEASE6_PREFERRED_LIFETIME` 742 | - `KEA_LEASE6_IAID` 743 | 744 | ### [`lease6_expire`](https://jenkins.isc.org/job/Kea_doc/doxygen/d1/d02/dhcpv6Hooks.html#dhcpv6HooksLease6Expire) 745 | 746 | - `KEA_LEASE6_TYPE` 747 | - `KEA_LEASE6_ADDRESS` 748 | - `KEA_LEASE6_DELEGATED_PREFIX` 749 | - `KEA_LEASE6_DELEGATED_PREFIXLEN` 750 | - `KEA_LEASE6_CLIENT_DUID` 751 | - `KEA_LEASE6_HWADDR` 752 | - `KEA_LEASE6_HOSTNAME` 753 | - `KEA_LEASE6_STATE` 754 | - `KEA_LEASE6_IS_EXPIRED` 755 | - `KEA_LEASE6_CLIENT_LAST_TRANSMISSION` 756 | - `KEA_LEASE6_VALID_LIFETIME` 757 | - `KEA_LEASE6_PREFERRED_LIFETIME` 758 | - `KEA_LEASE6_IAID` 759 | - `KEA_REMOVE_LEASE` 760 | 761 | ### [`lease6_recover`](https://jenkins.isc.org/job/Kea_doc/doxygen/d1/d02/dhcpv6Hooks.html#dhcpv6HooksLease6Recover) 762 | 763 | - `KEA_LEASE6_TYPE` 764 | - `KEA_LEASE6_ADDRESS` 765 | - `KEA_LEASE6_DELEGATED_PREFIX` 766 | - `KEA_LEASE6_DELEGATED_PREFIXLEN` 767 | - `KEA_LEASE6_CLIENT_DUID` 768 | - `KEA_LEASE6_HWADDR` 769 | - `KEA_LEASE6_HOSTNAME` 770 | - `KEA_LEASE6_STATE` 771 | - `KEA_LEASE6_IS_EXPIRED` 772 | - `KEA_LEASE6_CLIENT_LAST_TRANSMISSION` 773 | - `KEA_LEASE6_VALID_LIFETIME` 774 | - `KEA_LEASE6_PREFERRED_LIFETIME` 775 | - `KEA_LEASE6_IAID` 776 | 777 | 778 | # TODO 779 | 780 | - take stdout/stderr of script and turn it into proper Kea logs 781 | - agree on a consistent terminology: 782 | - should a "prefix" variable contain the prefixlen (2001:db8::/48) or just the base address (2001:db8::)? 783 | - also call the script at load/unload 784 | - figure out how to call several scripts (loading the hook multiple times doesn't seem to work) 785 | - allow to configure which hook points will trigger the script 786 | - take into account the return code of the script to set the status 787 | of the callout (this should be configurable to avoid surprises...). 788 | 789 | Some bugs to investigate/fix in Kea: 790 | 791 | - `lease6_select` is called twice (once with `IA_NA` and once with `IA_PD`), but 792 | other functions (`lease6_renew`, `lease6_release`, `lease6_expire`) are only 793 | called with `IA_PD`. 794 | - when an address reservation is changed for a given client, `lease6_expire` is never 795 | called for the old address. 796 | -------------------------------------------------------------------------------- /examples/debug.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | LOGFILE=/tmp/kea-hook-runscript-debug.log 4 | 5 | echo "== $1 ==" >> $LOGFILE 6 | date >> $LOGFILE 7 | env >> $LOGFILE 8 | echo >> $LOGFILE 9 | echo >> $LOGFILE 10 | -------------------------------------------------------------------------------- /examples/ipv6_prefix_delegation/README.md: -------------------------------------------------------------------------------- 1 | # Route management for IPv6 delegated prefixes 2 | 3 | The goal here is to add/remove static IPv6 routes in the kernel whenever Kea delegates 4 | an IPv6 prefix through DHCPv6-PD. 5 | 6 | This is achieved by running [ipv6routes.sh](ipv6-routes.sh) with kea-hook-runscript. 7 | 8 | The routes added by the script can then be picked up by a routing daemon 9 | (e.g. [Bird](http://bird.network.cz/)) and propagated in a IGP like OSPF. 10 | 11 | **Note:** the script also inserts routes for `IA_NA` addresses, because it is necessary 12 | in our setup. If you only need routes for delegated prefixes, adapt the script accordingly. 13 | 14 | ## Limitations 15 | 16 | There is a potential issue when the IPv6 prefix reserved to a client is changed (e.g. if it is 17 | modified in the Postgresql data source). In that case, `lease6_release` / `lease6_expire` is 18 | never called with the old prefix, so the corresponding route is never removed from the kernel. 19 | It is not clear whether it is a bug in Kea or if it is related to the specific setup of the author. 20 | In any case, this is something to watch out for. 21 | -------------------------------------------------------------------------------- /examples/ipv6_prefix_delegation/ipv6-routes.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # This script adds and removes IPv6 routes in the Linux kernel whenever a DHCP client 3 | # gets a lease or a lease expires. 4 | 5 | # Protocol to use in "ip -6 route" 6 | PROTO=static 7 | 8 | add_ipv6_routes() 9 | { 10 | if [ "$KEA_LEASE6_TYPE" = "IA_NA" ]; then 11 | # Add interface route towards client 12 | ip -6 route replace "${KEA_LEASE6_ADDRESS}"/64 dev "${KEA_QUERY6_INTERFACE}" proto "${PROTO}" 13 | fi 14 | if [ "$KEA_LEASE6_TYPE" = "IA_PD" ]; then 15 | # Add route for delegated prefix (next hop is the client) 16 | ip -6 route replace "${KEA_LEASE6_DELEGATED_PREFIX}" via "${KEA_QUERY6_REMOTE_ADDRESS}" dev "${KEA_QUERY6_INTERFACE}" proto "${PROTO}" 17 | fi 18 | } 19 | 20 | remove_ipv6_routes() 21 | { 22 | if [ "$KEA_LEASE6_TYPE" = "IA_NA" ]; then 23 | ip -6 route delete "${KEA_LEASE6_ADDRESS}"/64 proto "${PROTO}" 24 | fi 25 | if [ "$KEA_LEASE6_TYPE" = "IA_PD" ]; then 26 | ip -6 route delete "${KEA_LEASE6_DELEGATED_PREFIX}" proto "${PROTO}" 27 | fi 28 | } 29 | 30 | case "$1" in 31 | "lease6_select") 32 | # Only add route if FAKE_ALLOCATION is set to 0 33 | [ "${KEA_FAKE_ALLOCATION}" = "0" ] || break 34 | add_ipv6_routes 35 | ;; 36 | "lease6_renew") 37 | add_ipv6_routes 38 | ;; 39 | "lease6_release"|"lease6_expire") 40 | remove_ipv6_routes 41 | ;; 42 | esac 43 | -------------------------------------------------------------------------------- /examples/ipv6_prefix_delegation/kea-dhcp6.conf: -------------------------------------------------------------------------------- 1 | { 2 | "Dhcp6": 3 | { 4 | "interfaces-config": { 5 | "interfaces": [ "eth0" ] 6 | }, 7 | 8 | "hooks-libraries": [ 9 | { 10 | "library": "/path/to/kea-hook-runscript/kea-hook-runscript.so", 11 | "parameters": { 12 | "script": "/etc/kea/ipv6-routes.sh" 13 | } 14 | } 15 | ], 16 | 17 | "lease-database": { 18 | "type": "memfile" 19 | }, 20 | 21 | /* Only use client MAC address, not Client ID */ 22 | "match-client-id": false, 23 | 24 | "renew-timer": 1100, 25 | "rebind-timer": 1150, 26 | "preferred-lifetime": 1200, 27 | "valid-lifetime": 1400, 28 | 29 | /* Only use mac address to identify clients */ 30 | "host-reservation-identifiers": ["hw-address"], 31 | 32 | "subnet6": [ 33 | { 34 | /* Add your IPv6 subnet declaration here */ 35 | } 36 | ] 37 | }, 38 | 39 | "Logging": 40 | { 41 | "loggers": [ 42 | { 43 | "name": "kea-dhcp6", 44 | "output_options": [ 45 | { 46 | "output": "stdout" 47 | #"output": "/var/log/kea-dhcp6.log" 48 | } 49 | , 50 | "severity": "DEBUG", 51 | "debuglevel": 0 52 | } 53 | ] 54 | } 55 | 56 | } 57 | 58 | -------------------------------------------------------------------------------- /examples/slash32_leases/README.md: -------------------------------------------------------------------------------- 1 | # Handing out IPv4 addresses in /32 subnets 2 | 3 | The goal of this example is to lease IPv4 addresses individually (/32 design). 4 | This is essentially a "out-of-subnet" reservation mode, because clients do not 5 | need to be in the same IP subnet as the DHCP server. This is mostly 6 | useful to hand out public IPv4 addresses to customers. 7 | 8 | This method is very flexible: 9 | 10 | - each DHCP server can handle clients with very different IP addresses 11 | (there is no need to partition the network with static subnets) 12 | - clients can "move" from one DHCP server to another while keeping 13 | their IP address (even when moving to a different layer 2 network), 14 | provided that a dynamic routing protocol is used. 15 | 16 | The script adds a route in the Linux kernel whenever a DHCP client gets a lease, 17 | so that the new client's IP address becomes reachable. Similarly, when an IP address 18 | is released or a lease expires, the route is removed from the kernel. This assumes 19 | that clients are directly connected to Kea (same layer 2 network), so it will not 20 | work with DHCP relays. In such cases, the DHCP relay itself should manage routes 21 | towards clients, not Kea. 22 | 23 | The routes added by the script can then be picked up by a routing daemon 24 | (e.g. [Bird](http://bird.network.cz/)) and propagated in a IGP like OSPF. 25 | 26 | On the client side, DHCP option 121 is used so that the client can use the 27 | DHCP server as gateway without being in the same subnet. Assuming the DHCP 28 | server's IP is `10.250.250.1/32` and the client's leased IP is `192.0.2.201/32`, 29 | the routing table of the client will look like this: 30 | 31 | 10.250.250.1 dev eth0 proto kernel scope link src 192.0.2.201 metric 224 32 | default via 10.250.250.1 dev eth0 src 192.0.2.201 metric 224 33 | 34 | -------------------------------------------------------------------------------- /examples/slash32_leases/kea-dhcp4.conf: -------------------------------------------------------------------------------- 1 | { 2 | "Dhcp4": 3 | { 4 | "interfaces-config": { 5 | "interfaces": [ "eth0" ] 6 | }, 7 | 8 | "hooks-libraries": [ 9 | { 10 | "library": "/path/to/kea-hook-runscript/kea-hook-runscript.so", 11 | "parameters": { 12 | "script": "/path/to/script.sh" 13 | } 14 | } 15 | ], 16 | 17 | "lease-database": { 18 | "type": "memfile" 19 | }, 20 | 21 | /* Option 121 definition */ 22 | "option-def": [ 23 | { 24 | "name": "rfc3442-classless-static-routes", 25 | "code": 121, 26 | "space": "dhcp4", 27 | "type": "record", 28 | "array": false, 29 | /* This is not a general option 121, it is specific to this usage with two routes: */ 30 | /* Subnet length of destination1 (32), destination1 (X/32), router1, destination2 (0/0), router2 */ 31 | "record-types": "uint8, ipv4-address, ipv4-address, uint8, ipv4-address" 32 | } 33 | ], 34 | 35 | 36 | /* Only use client MAC address, not Client ID */ 37 | "match-client-id": false, 38 | 39 | /* Set short lifetime, so that routes are removed from the kernel shortly after 40 | a client leaves the network without sending a DHCPRELEASE. */ 41 | "valid-lifetime": 300, 42 | "renew-timer": 220, 43 | "rebind-timer": 220, 44 | 45 | "host-reservation-identifiers": ["hw-address"], 46 | "subnet4": [ 47 | { 48 | /* Hack so that Kea hands out addresses in /32 subnets */ 49 | "subnet": "0.0.0.0/32", 50 | 51 | /* Send appropriate routes to the client (you should adapt this, see README) */ 52 | "option-data": [ 53 | { 54 | "name": "rfc3442-classless-static-routes", 55 | /* 10.250.250.1/32 via 0.0.0.0, 0/0 via 10.250.250.1 */ 56 | "data": "32, 10.250.250.1, 0.0.0.0, 0, 10.250.250.1" 57 | } 58 | ], 59 | 60 | "interface": "eth0", 61 | "reservation-mode": "out-of-pool", 62 | /* IP addresses reserved for clients, can be any /32 address. */ 63 | "reservations": [ 64 | { 65 | "hw-address": "96:ec:f8:ac:d8:bf", 66 | "ip-address": "192.0.2.201" 67 | }, 68 | { 69 | "hw-address": "aa:bb:cc:dd:ee:ff", 70 | "ip-address": "203.0.113.12" 71 | } 72 | ] 73 | } 74 | ] 75 | }, 76 | 77 | "Logging": 78 | { 79 | "loggers": [ 80 | { 81 | "name": "kea-dhcp4", 82 | "output_options": [ 83 | { 84 | "output": "stdout" 85 | #"output": "/var/log/kea-dhcp4.log" 86 | } 87 | ], 88 | "severity": "DEBUG", 89 | "debuglevel": 0 90 | } 91 | ] 92 | } 93 | 94 | } 95 | 96 | -------------------------------------------------------------------------------- /examples/slash32_leases/script.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # This script adds and removes routes in the Linux kernel whenever a DHCP client 3 | # gets a lease or a lease expires. 4 | 5 | # Protocol to use in "ip route" 6 | PROTO=static 7 | 8 | case "$1" in 9 | "lease4_select") 10 | # Only add route if FAKE_ALLOCATION is set to 0 11 | [ "${KEA_FAKE_ALLOCATION}" = "0" ] || break 12 | ip route replace "${KEA_LEASE4_ADDRESS}"/32 dev "${KEA_QUERY4_INTERFACE}" proto "${PROTO}" 13 | ;; 14 | "lease4_renew") 15 | ip route replace "${KEA_LEASE4_ADDRESS}"/32 dev "${KEA_QUERY4_INTERFACE}" proto "${PROTO}" 16 | ;; 17 | "lease4_release"|"lease4_expire") 18 | ip route del "${KEA_LEASE4_ADDRESS}"/32 proto "${PROTO}" 19 | ;; 20 | "lease4_decline") 21 | echo "$(date -R): received DHCPDECLINE on ${KEA_QUERY4_INTERFACE} from ${KEA_QUERY4_HWADDR} about ${KEA_LEASE4_ADDRESS}." >&2 22 | echo "Probably a duplicate IP assignment, not removing any route." >&2 23 | echo "Existing route for this IP: $(ip route show ${KEA_LEASE4_ADDRESS})" >&2 24 | echo "Existing ARP entry for this IP: $(ip neigh show ${KEA_LEASE4_ADDRESS})" >&2 25 | ;; 26 | esac 27 | -------------------------------------------------------------------------------- /release.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | RELEASE="$1" 6 | 7 | [ -z "$RELEASE" ] && { echo "usage: $0 X.Y.Z"; exit 1; } 8 | 9 | # Check repo is clean 10 | git update-index -q --refresh 11 | git diff-files --quiet || { echo "error: ensure git repo has no changes"; exit 1; } 12 | 13 | # TODO: regenerate message files to make sure they are up-to-date 14 | 15 | git tag v"$RELEASE" 16 | git archive --prefix=kea-hook-runscript-"$RELEASE"/ -o kea-hook-runscript-"$RELEASE".tar.gz v"$RELEASE" 17 | -------------------------------------------------------------------------------- /src/callouts.cc: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2017-2019 by Baptiste Jonglez 2 | * 3 | * This Source Code Form is subject to the terms of the Mozilla Public 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this 5 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | */ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | #include 18 | 19 | #include "runscript.h" 20 | 21 | using namespace isc::dhcp; 22 | using namespace isc::hooks; 23 | 24 | extern "C" { 25 | 26 | /* These are helpers that extract relevant information from Kea data 27 | * structures and store them in environment variables. */ 28 | void extract_bool(std::vector& env, const std::string variable, bool value) 29 | { 30 | env.push_back(variable + "=" + std::string(value ? "1" : "0")); 31 | } 32 | 33 | /* Extract information from a DHCPv4 packet (query received, or response 34 | * about to be sent) */ 35 | void extract_pkt4(std::vector& env, const std::string envprefix, const Pkt4Ptr pkt4) 36 | { 37 | /* General information */ 38 | env.push_back(envprefix + "TYPE=" + std::string(pkt4->getName())); 39 | env.push_back(envprefix + "INTERFACE=" + pkt4->getIface()); 40 | env.push_back(envprefix + "IFINDEX=" + std::to_string(pkt4->getIndex())); 41 | /* Hardware address */ 42 | HWAddrPtr hwaddr = pkt4->getHWAddr(); 43 | if (hwaddr) { 44 | env.push_back(envprefix + "HWADDR=" + hwaddr->toText(false)); 45 | env.push_back(envprefix + "HWADDR_TYPE=" + std::to_string(hwaddr->htype_)); 46 | env.push_back(envprefix + "HWADDR_SOURCE=" + std::to_string(hwaddr->source_)); 47 | } else { 48 | env.push_back(envprefix + "HWADDR="); 49 | env.push_back(envprefix + "HWADDR_TYPE="); 50 | env.push_back(envprefix + "HWADDR_SOURCE="); 51 | } 52 | /* Misc */ 53 | env.push_back(envprefix + "CIADDR=" + pkt4->getCiaddr().toText()); 54 | env.push_back(envprefix + "SIADDR=" + pkt4->getSiaddr().toText()); 55 | env.push_back(envprefix + "YIADDR=" + pkt4->getYiaddr().toText()); 56 | env.push_back(envprefix + "GIADDR=" + pkt4->getGiaddr().toText()); 57 | env.push_back(envprefix + "RELAYED=" + std::to_string(pkt4->isRelayed())); 58 | env.push_back(envprefix + "RELAY_HOPS=" + std::to_string(pkt4->getHops())); 59 | 60 | /* Specific Options */ 61 | OptionPtr option60 = pkt4->getOption(60); 62 | if (option60) { 63 | env.push_back(envprefix + "OPTION60=" + option60->toString()); 64 | } 65 | 66 | OptionPtr rai = pkt4->getOption(82); 67 | if (rai) { 68 | env.push_back(envprefix + "RAI=" + rai->toHexString()); 69 | 70 | OptionPtr circuit_id = rai->getOption(RAI_OPTION_AGENT_CIRCUIT_ID); 71 | if (circuit_id) { 72 | env.push_back(envprefix + "RAI_CIRCUIT_ID=" + circuit_id->toHexString()); 73 | } 74 | 75 | OptionPtr remote_id = rai->getOption(RAI_OPTION_REMOTE_ID); 76 | if (remote_id) { 77 | env.push_back(envprefix + "RAI_REMOTE_ID=" + remote_id->toHexString()); 78 | } 79 | } 80 | } 81 | 82 | void extract_query4(std::vector& env, const Pkt4Ptr query) 83 | { 84 | extract_pkt4(env, "KEA_QUERY4_", query); 85 | } 86 | 87 | void extract_response4(std::vector& env, const Pkt4Ptr response) 88 | { 89 | extract_pkt4(env, "KEA_RESPONSE4_", response); 90 | } 91 | 92 | /* Extract information from a DHCPv6 packet (query received, or response 93 | * about to be sent) */ 94 | void extract_pkt6(std::vector& env, const std::string envprefix, const Pkt6Ptr pkt6) 95 | { 96 | /* General information */ 97 | env.push_back(envprefix + "TYPE=" + std::string(pkt6->getName())); 98 | env.push_back(envprefix + "INTERFACE=" + pkt6->getIface()); 99 | env.push_back(envprefix + "IFINDEX=" + std::to_string(pkt6->getIndex())); 100 | HWAddrPtr hwaddr = pkt6->getMAC(HWAddr::HWADDR_SOURCE_ANY); 101 | if (hwaddr) { 102 | env.push_back(envprefix + "HWADDR=" + hwaddr->toText(false)); 103 | env.push_back(envprefix + "HWADDR_TYPE=" + std::to_string(hwaddr->htype_)); 104 | env.push_back(envprefix + "HWADDR_SOURCE=" + std::to_string(hwaddr->source_)); 105 | } else { 106 | env.push_back(envprefix + "HWADDR="); 107 | env.push_back(envprefix + "HWADDR_TYPE="); 108 | env.push_back(envprefix + "HWADDR_SOURCE="); 109 | } 110 | env.push_back(envprefix + "LOCAL_ADDRESS=" + pkt6->getLocalAddr().toText()); 111 | env.push_back(envprefix + "LOCAL_PORT=" + std::to_string(pkt6->getLocalPort())); 112 | env.push_back(envprefix + "REMOTE_ADDRESS=" + pkt6->getRemoteAddr().toText()); 113 | env.push_back(envprefix + "REMOTE_PORT=" + std::to_string(pkt6->getRemotePort())); 114 | env.push_back(envprefix + "LABEL=" + pkt6->getLabel()); 115 | env.push_back(envprefix + "TRANSACTION_ID=" + std::to_string(pkt6->getTransid())); 116 | /* TODO */ 117 | env.push_back(envprefix + "DUID="); 118 | /* TODO: all options? Only common ones? Which format? */ 119 | /* TODO */ 120 | env.push_back(envprefix + "DEBUG=" + pkt6->toText()); 121 | } 122 | 123 | void extract_query6(std::vector& env, const Pkt6Ptr query) 124 | { 125 | extract_pkt6(env, "KEA_QUERY6_", query); 126 | } 127 | 128 | 129 | void extract_response6(std::vector& env, const Pkt6Ptr response) 130 | { 131 | extract_pkt6(env, "KEA_RESPONSE6_", response); 132 | } 133 | 134 | void extract_subnet4(std::vector& env, const Subnet4Ptr subnet) 135 | { 136 | /* The subnet given by Kea might be NULL, this seems to happen when 137 | * Kea fails to find a matching subnet for a client request. */ 138 | if (subnet != NULL) { 139 | env.push_back("KEA_SUBNET4=" + subnet->toText()); 140 | std::pair prefix = subnet->get(); 141 | env.push_back("KEA_SUBNET4_PREFIX=" + prefix.first.toText()); 142 | env.push_back("KEA_SUBNET4_PREFIXLEN=" + std::to_string(prefix.second)); 143 | } else { 144 | env.push_back("KEA_SUBNET4="); 145 | env.push_back("KEA_SUBNET4_PREFIX="); 146 | env.push_back("KEA_SUBNET4_PREFIXLEN="); 147 | } 148 | } 149 | 150 | void extract_subnet6(std::vector& env, const Subnet6Ptr subnet) 151 | { 152 | if (subnet != NULL) { 153 | env.push_back("KEA_SUBNET6=" + subnet->toText()); 154 | std::pair prefix = subnet->get(); 155 | env.push_back("KEA_SUBNET6_PREFIX=" + prefix.first.toText()); 156 | env.push_back("KEA_SUBNET6_PREFIXLEN=" + std::to_string(prefix.second)); 157 | } else { 158 | env.push_back("KEA_SUBNET6="); 159 | env.push_back("KEA_SUBNET6_PREFIX="); 160 | env.push_back("KEA_SUBNET6_PREFIXLEN="); 161 | } 162 | } 163 | 164 | void extract_lease4(std::vector& env, const Lease4Ptr lease) 165 | { 166 | env.push_back("KEA_LEASE4_TYPE=V4"); 167 | env.push_back("KEA_LEASE4_STATE=" + lease->basicStatesToText(lease->state_)); 168 | extract_bool(env, "KEA_LEASE4_IS_EXPIRED", lease->expired()); 169 | env.push_back("KEA_LEASE4_ADDRESS=" + lease->addr_.toText()); 170 | if (lease->hwaddr_) { 171 | env.push_back("KEA_LEASE4_HWADDR=" + lease->hwaddr_->toText(false)); 172 | } else { 173 | env.push_back("KEA_LEASE4_HWADDR="); 174 | } 175 | env.push_back("KEA_LEASE4_HOSTNAME=" + lease->hostname_); 176 | env.push_back("KEA_LEASE4_CLIENT_LAST_TRANSMISSION=" + std::to_string(lease->cltt_)); 177 | env.push_back("KEA_LEASE4_VALID_LIFETIME=" + std::to_string(lease->valid_lft_)); 178 | env.push_back("KEA_LEASE4_DEBUG=" + lease->toText()); 179 | } 180 | 181 | void extract_lease6(std::vector& env, const Lease6Ptr lease) 182 | { 183 | env.push_back("KEA_LEASE6_TYPE=" + lease->typeToText(lease->type_)); 184 | env.push_back("KEA_LEASE6_STATE=" + lease->basicStatesToText(lease->state_)); 185 | extract_bool(env, "KEA_LEASE6_IS_EXPIRED", lease->expired()); 186 | env.push_back("KEA_LEASE6_ADDRESS=" + lease->addr_.toText()); 187 | if (lease->type_ == Lease::TYPE_PD) { 188 | env.push_back("KEA_LEASE6_DELEGATED_PREFIX=" + lease->addr_.toText() + "/" + std::to_string(lease->prefixlen_)); 189 | env.push_back("KEA_LEASE6_DELEGATED_PREFIXLEN=" + std::to_string(lease->prefixlen_)); 190 | } 191 | if (lease->hwaddr_) { 192 | env.push_back("KEA_LEASE6_HWADDR=" + lease->hwaddr_->toText(false)); 193 | } else { 194 | env.push_back("KEA_LEASE6_HWADDR="); 195 | } 196 | env.push_back("KEA_LEASE6_HOSTNAME=" + lease->hostname_); 197 | env.push_back("KEA_LEASE6_CLIENT_DUID=" + lease->duid_->toText()); 198 | env.push_back("KEA_LEASE6_CLIENT_LAST_TRANSMISSION=" + std::to_string(lease->cltt_)); 199 | env.push_back("KEA_LEASE6_VALID_LIFETIME=" + std::to_string(lease->valid_lft_)); 200 | env.push_back("KEA_LEASE6_PREFERRED_LIFETIME=" + std::to_string(lease->preferred_lft_)); 201 | env.push_back("KEA_LEASE6_IAID=" + std::to_string(lease->iaid_)); 202 | env.push_back("KEA_LEASE6_DEBUG=" + lease->toText()); 203 | } 204 | 205 | /* IPv4 callouts */ 206 | int pkt4_receive(CalloutHandle& handle) { 207 | std::vector env; 208 | Pkt4Ptr query; 209 | handle.getArgument("query4", query); 210 | extract_query4(env, query); 211 | /* Run script */ 212 | int ret; 213 | ret = run_script("pkt4_receive", env); 214 | return 0; 215 | } 216 | 217 | int pkt4_send(CalloutHandle& handle) { 218 | std::vector env; 219 | Pkt4Ptr response; 220 | Pkt4Ptr query; 221 | handle.getArgument("response4", response); 222 | extract_response4(env, response); 223 | handle.getArgument("query4", query); 224 | extract_query4(env, query); 225 | /* Run script */ 226 | int ret; 227 | ret = run_script("pkt4_send", env); 228 | return 0; 229 | } 230 | 231 | int subnet4_select(CalloutHandle& handle) { 232 | std::vector env; 233 | Pkt4Ptr query; 234 | Subnet4Ptr subnet; 235 | handle.getArgument("query4", query); 236 | extract_query4(env, query); 237 | handle.getArgument("subnet4", subnet); 238 | extract_subnet4(env, subnet); 239 | /* Run script */ 240 | int ret; 241 | ret = run_script("subnet4_select", env); 242 | return 0; 243 | } 244 | 245 | int lease4_select(CalloutHandle& handle) { 246 | std::vector env; 247 | Pkt4Ptr query; 248 | Subnet4Ptr subnet; 249 | bool fake_allocation; 250 | Lease4Ptr lease; 251 | handle.getArgument("query4", query); 252 | extract_query4(env, query); 253 | handle.getArgument("subnet4", subnet); 254 | extract_subnet4(env, subnet); 255 | handle.getArgument("fake_allocation", fake_allocation); 256 | extract_bool(env, "KEA_FAKE_ALLOCATION", fake_allocation); 257 | handle.getArgument("lease4", lease); 258 | extract_lease4(env, lease); 259 | /* Run script */ 260 | int ret; 261 | ret = run_script("lease4_select", env); 262 | return 0; 263 | } 264 | 265 | int lease4_renew(CalloutHandle& handle) { 266 | std::vector env; 267 | Pkt4Ptr query; 268 | Subnet4Ptr subnet; 269 | Lease4Ptr lease; 270 | handle.getArgument("query4", query); 271 | extract_query4(env, query); 272 | handle.getArgument("subnet4", subnet); 273 | extract_subnet4(env, subnet); 274 | /* TODO: what is this? Is it different from what is in the query? */ 275 | //handle.getArgument("clientid", XX); 276 | //handle.getArgument("hwaddr", XX); 277 | handle.getArgument("lease4", lease); 278 | extract_lease4(env, lease); 279 | /* Run script */ 280 | int ret; 281 | ret = run_script("lease4_renew", env); 282 | return 0; 283 | } 284 | 285 | int lease4_release(CalloutHandle& handle) { 286 | std::vector env; 287 | Pkt4Ptr query; 288 | Lease4Ptr lease; 289 | handle.getArgument("query4", query); 290 | extract_query4(env, query); 291 | handle.getArgument("lease4", lease); 292 | extract_lease4(env, lease); 293 | /* Run script */ 294 | int ret; 295 | ret = run_script("lease4_release", env); 296 | return 0; 297 | } 298 | 299 | int lease4_decline(CalloutHandle& handle) { 300 | std::vector env; 301 | Pkt4Ptr query; 302 | Lease4Ptr lease; 303 | handle.getArgument("query4", query); 304 | extract_query4(env, query); 305 | handle.getArgument("lease4", lease); 306 | extract_lease4(env, lease); 307 | /* Run script */ 308 | int ret; 309 | ret = run_script("lease4_decline", env); 310 | return 0; 311 | } 312 | 313 | int lease4_expire(CalloutHandle& handle) { 314 | std::vector env; 315 | Lease4Ptr lease; 316 | bool remove_lease; 317 | handle.getArgument("lease4", lease); 318 | extract_lease4(env, lease); 319 | handle.getArgument("remove_lease", remove_lease); 320 | extract_bool(env, "KEA_REMOVE_LEASE", remove_lease); 321 | /* Run script */ 322 | int ret; 323 | ret = run_script("lease4_expire", env); 324 | return 0; 325 | } 326 | 327 | int lease4_recover(CalloutHandle& handle) { 328 | std::vector env; 329 | Lease4Ptr lease; 330 | handle.getArgument("lease4", lease); 331 | extract_lease4(env, lease); 332 | /* Run script */ 333 | int ret; 334 | ret = run_script("lease4_recover", env); 335 | return 0; 336 | } 337 | 338 | /* IPv6 callouts */ 339 | int pkt6_receive(CalloutHandle& handle) { 340 | std::vector env; 341 | Pkt6Ptr query; 342 | handle.getArgument("query6", query); 343 | extract_query6(env, query); 344 | /* Run script */ 345 | int ret; 346 | ret = run_script("pkt6_receive", env); 347 | return 0; 348 | } 349 | 350 | int pkt6_send(CalloutHandle& handle) { 351 | std::vector env; 352 | Pkt6Ptr query, response; 353 | handle.getArgument("query6", query); 354 | extract_query6(env, query); 355 | handle.getArgument("response6", response); 356 | extract_response6(env, response); 357 | /* Run script */ 358 | int ret; 359 | ret = run_script("pkt6_send", env); 360 | return 0; 361 | } 362 | 363 | int subnet6_select(CalloutHandle& handle) { 364 | std::vector env; 365 | Pkt6Ptr query; 366 | Subnet6Ptr subnet; 367 | handle.getArgument("query6", query); 368 | extract_query6(env, query); 369 | handle.getArgument("subnet6", subnet); 370 | extract_subnet6(env, subnet); 371 | /* Run script */ 372 | int ret; 373 | ret = run_script("subnet6_select", env); 374 | return 0; 375 | } 376 | 377 | int lease6_select(CalloutHandle& handle) { 378 | std::vector env; 379 | Pkt6Ptr query; 380 | Subnet6Ptr subnet; 381 | bool fake_allocation; 382 | Lease6Ptr lease; 383 | handle.getArgument("query6", query); 384 | extract_query6(env, query); 385 | handle.getArgument("subnet6", subnet); 386 | extract_subnet6(env, subnet); 387 | handle.getArgument("fake_allocation", fake_allocation); 388 | extract_bool(env, "KEA_FAKE_ALLOCATION", fake_allocation); 389 | handle.getArgument("lease6", lease); 390 | extract_lease6(env, lease); 391 | /* Run script */ 392 | int ret; 393 | ret = run_script("lease6_select", env); 394 | return 0; 395 | } 396 | 397 | int lease6_renew(CalloutHandle& handle) { 398 | std::vector env; 399 | Pkt6Ptr query; 400 | Lease6Ptr lease; 401 | boost::shared_ptr ia_na; 402 | boost::shared_ptr ia_pd; 403 | handle.getArgument("query6", query); 404 | extract_query6(env, query); 405 | handle.getArgument("lease6", lease); 406 | extract_lease6(env, lease); 407 | try { 408 | handle.getArgument("ia_na", ia_na); 409 | /* TODO: use ia_na */ 410 | } catch (const NoSuchArgument&) { } 411 | try { 412 | handle.getArgument("ia_pd", ia_pd); 413 | /* TODO: use ia_pd */ 414 | } catch (const NoSuchArgument&) { } 415 | /* Run script */ 416 | int ret; 417 | ret = run_script("lease6_renew", env); 418 | return 0; 419 | } 420 | 421 | int lease6_rebind(CalloutHandle& handle) { 422 | std::vector env; 423 | Pkt6Ptr query; 424 | Lease6Ptr lease; 425 | boost::shared_ptr ia_na; 426 | boost::shared_ptr ia_pd; 427 | handle.getArgument("query6", query); 428 | extract_query6(env, query); 429 | handle.getArgument("lease6", lease); 430 | extract_lease6(env, lease); 431 | try { 432 | handle.getArgument("ia_na", ia_na); 433 | /* TODO: use ia_na */ 434 | } catch (const NoSuchArgument&) { } 435 | try { 436 | handle.getArgument("ia_pd", ia_pd); 437 | /* TODO: use ia_pd */ 438 | } catch (const NoSuchArgument&) { } 439 | /* Run script */ 440 | int ret; 441 | ret = run_script("lease6_rebind", env); 442 | return 0; 443 | } 444 | 445 | int lease6_decline(CalloutHandle& handle) { 446 | std::vector env; 447 | Pkt6Ptr query; 448 | Lease6Ptr lease; 449 | handle.getArgument("query6", query); 450 | extract_query6(env, query); 451 | handle.getArgument("lease6", lease); 452 | extract_lease6(env, lease); 453 | /* Run script */ 454 | int ret; 455 | ret = run_script("lease6_decline", env); 456 | return 0; 457 | } 458 | 459 | int lease6_release(CalloutHandle& handle) { 460 | std::vector env; 461 | Pkt6Ptr query; 462 | Lease6Ptr lease; 463 | handle.getArgument("query6", query); 464 | extract_query6(env, query); 465 | handle.getArgument("lease6", lease); 466 | extract_lease6(env, lease); 467 | /* Run script */ 468 | int ret; 469 | ret = run_script("lease6_release", env); 470 | return 0; 471 | } 472 | 473 | int lease6_expire(CalloutHandle& handle) { 474 | std::vector env; 475 | Lease6Ptr lease; 476 | bool remove_lease; 477 | handle.getArgument("lease6", lease); 478 | extract_lease6(env, lease); 479 | handle.getArgument("remove_lease", remove_lease); 480 | extract_bool(env, "KEA_REMOVE_LEASE", remove_lease); 481 | /* Run script */ 482 | int ret; 483 | ret = run_script("lease6_expire", env); 484 | return 0; 485 | } 486 | 487 | int lease6_recover(CalloutHandle& handle) { 488 | std::vector env; 489 | Lease6Ptr lease; 490 | handle.getArgument("lease6", lease); 491 | extract_lease6(env, lease); 492 | /* Run script */ 493 | int ret; 494 | ret = run_script("lease6_recover", env); 495 | return 0; 496 | } 497 | 498 | } // end extern "C" 499 | -------------------------------------------------------------------------------- /src/common.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2017-2019 by Baptiste Jonglez 2 | * 3 | * This Source Code Form is subject to the terms of the Mozilla Public 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this 5 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | */ 7 | 8 | #include 9 | 10 | extern "C" { 11 | 12 | /* From load.cc */ 13 | 14 | /* Path of the script to be run in hooks. */ 15 | extern std::string script_path; 16 | /* Name of the script (without the leading directory). */ 17 | extern std::string script_name; 18 | /* Wait for script to finish executing */ 19 | extern bool script_wait; 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/load.cc: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2017-2019 by Baptiste Jonglez 2 | * 3 | * This Source Code Form is subject to the terms of the Mozilla Public 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this 5 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | */ 7 | 8 | #include 9 | #include 10 | 11 | #include "logger.h" 12 | #include "common.h" 13 | 14 | using namespace isc::hooks; 15 | using namespace isc::data; 16 | 17 | /* Path of the script to be run in hooks, accessed by the other files of 18 | * this library. */ 19 | std::string script_path; 20 | /* Name of the script (without the leading directory). */ 21 | std::string script_name; 22 | /* Wait for script to finish executing */ 23 | bool script_wait; 24 | 25 | extern "C" { 26 | 27 | int load(LibraryHandle& handle) { 28 | ConstElementPtr script = handle.getParameter("script"); 29 | if (!script) { 30 | LOG_ERROR(runscript_logger, RUNSCRIPT_MISSING_PARAM).arg("script"); 31 | return 1; 32 | } 33 | if (script->getType() != Element::string) { 34 | LOG_ERROR(runscript_logger, RUNSCRIPT_MISTYPED_PARAM).arg("script"); 35 | return 1; 36 | } 37 | script_path = script->stringValue(); 38 | script_name = script_path.substr(script_path.find_last_of('/') + 1); 39 | 40 | ConstElementPtr wait = handle.getParameter("wait"); 41 | if (!wait) { 42 | script_wait = true; 43 | } else if (wait->getType() != Element::boolean) { 44 | LOG_ERROR(runscript_logger, RUNSCRIPT_MISTYPED_PARAM).arg("wait"); 45 | return 1; 46 | } else { 47 | script_wait = wait->boolValue(); 48 | } 49 | 50 | /* Install signal handler for non-wait case to avoid leaving zombie processes around */ 51 | if (!script_wait) { 52 | signal(SIGCHLD, SIG_IGN); 53 | } 54 | 55 | return 0; 56 | } 57 | 58 | } // end extern "C" 59 | -------------------------------------------------------------------------------- /src/logger.cc: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2017-2019 by Baptiste Jonglez 2 | * 3 | * This Source Code Form is subject to the terms of the Mozilla Public 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this 5 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | */ 7 | 8 | #include "logger.h" 9 | 10 | isc::log::Logger runscript_logger("hook-runscript"); 11 | -------------------------------------------------------------------------------- /src/logger.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2017-2019 by Baptiste Jonglez 2 | * 3 | * This Source Code Form is subject to the terms of the Mozilla Public 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this 5 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | */ 7 | 8 | #ifndef RUNSCRIPT_LOGGER_H 9 | #define RUNSCRIPT_LOGGER_H 10 | #include 11 | #include 12 | #include 13 | 14 | #include "messages.h" 15 | 16 | extern isc::log::Logger runscript_logger; 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /src/messages.cc: -------------------------------------------------------------------------------- 1 | // File created from src/messages.mes 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | extern const isc::log::MessageID RUNSCRIPT_EXEC_FAILED = "RUNSCRIPT_EXEC_FAILED"; 8 | extern const isc::log::MessageID RUNSCRIPT_FORK_FAILED = "RUNSCRIPT_FORK_FAILED"; 9 | extern const isc::log::MessageID RUNSCRIPT_MISSING_PARAM = "RUNSCRIPT_MISSING_PARAM"; 10 | extern const isc::log::MessageID RUNSCRIPT_MISTYPED_PARAM = "RUNSCRIPT_MISTYPED_PARAM"; 11 | extern const isc::log::MessageID RUNSCRIPT_WAITING_SCRIPT = "RUNSCRIPT_WAITING_SCRIPT"; 12 | extern const isc::log::MessageID RUNSCRIPT_WAITPID_FAILED = "RUNSCRIPT_WAITPID_FAILED"; 13 | 14 | namespace { 15 | 16 | const char* values[] = { 17 | "RUNSCRIPT_EXEC_FAILED", "exec() failed, please check that the script exists and is executable. Error: %1", 18 | "RUNSCRIPT_FORK_FAILED", "fork() failed with error: %1", 19 | "RUNSCRIPT_MISSING_PARAM", "required parameter \"%1\" missing in configuration", 20 | "RUNSCRIPT_MISTYPED_PARAM", "parameter \"%1\" in configuration has wrong type", 21 | "RUNSCRIPT_WAITING_SCRIPT", "the user-defined script is running, and the main process is currently waiting", 22 | "RUNSCRIPT_WAITPID_FAILED", "waitpid() failed with error: %1", 23 | NULL 24 | }; 25 | 26 | const isc::log::MessageInitializer initializer(values); 27 | 28 | } // Anonymous namespace 29 | 30 | -------------------------------------------------------------------------------- /src/messages.h: -------------------------------------------------------------------------------- 1 | // File created from src/messages.mes 2 | 3 | #ifndef MESSAGES_H 4 | #define MESSAGES_H 5 | 6 | #include 7 | 8 | extern const isc::log::MessageID RUNSCRIPT_EXEC_FAILED; 9 | extern const isc::log::MessageID RUNSCRIPT_FORK_FAILED; 10 | extern const isc::log::MessageID RUNSCRIPT_MISSING_PARAM; 11 | extern const isc::log::MessageID RUNSCRIPT_MISTYPED_PARAM; 12 | extern const isc::log::MessageID RUNSCRIPT_WAITING_SCRIPT; 13 | extern const isc::log::MessageID RUNSCRIPT_WAITPID_FAILED; 14 | 15 | #endif // MESSAGES_H 16 | -------------------------------------------------------------------------------- /src/messages.mes: -------------------------------------------------------------------------------- 1 | % RUNSCRIPT_MISSING_PARAM required parameter "%1" missing in configuration 2 | A required parameter of this library hook is missing in the configuration, 3 | which prevents the library from working correctly. 4 | 5 | % RUNSCRIPT_MISTYPED_PARAM parameter "%1" in configuration has wrong type 6 | A parameter of this library hook is defined in the configuration but has the 7 | wrong type. For instance, a string may have been passed as parameter while 8 | an integer was expected. 9 | 10 | % RUNSCRIPT_FORK_FAILED fork() failed with error: %1 11 | Running the user-defined script is done with fork + exec, and there was an error 12 | during the fork, possibly due to a lack of resources. 13 | 14 | % RUNSCRIPT_EXEC_FAILED exec() failed, please check that the script exists and is executable. Error: %1 15 | Attempting to execute the user-defined script failed. 16 | 17 | % RUNSCRIPT_WAITPID_FAILED waitpid() failed with error: %1 18 | The main hook process failed to wait for the child process to exit. 19 | The hook runs the user-defined script in a child process, and normally waits for it to exit. 20 | 21 | % RUNSCRIPT_WAITING_SCRIPT the user-defined script is running, and the main process is currently waiting 22 | The user-defined script has just been launched, and Kea is waiting for it to exit. 23 | This is useful to know, because if the user-defined script blocks, Kea will stay stuck 24 | at this point. 25 | -------------------------------------------------------------------------------- /src/runscript.cc: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2017-2019 by Baptiste Jonglez 2 | * 3 | * This Source Code Form is subject to the terms of the Mozilla Public 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this 5 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | */ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | #include "logger.h" 18 | #include "common.h" 19 | 20 | extern "C" { 21 | 22 | int run_script(std::string arg0, std::vector env) 23 | { 24 | /* Convert the vector containing environment variables to the format 25 | * expected by execle(). */ 26 | char const* envp[env.size() + 1]; 27 | for (int i = 0; i < env.size(); ++i) { 28 | envp[i] = env[i].c_str(); 29 | } 30 | envp[env.size()] = (char const*) NULL; 31 | 32 | /* fork() & execle() */ 33 | int ret, wstatus, exitcode; 34 | pid_t pid; 35 | pid = fork(); 36 | if (pid == -1) { 37 | LOG_ERROR(runscript_logger, RUNSCRIPT_FORK_FAILED).arg(strerror(errno)); 38 | return -1; 39 | } 40 | if (pid == 0) { 41 | /* Child process */ 42 | ret = execle(script_path.c_str(), script_name.c_str(), arg0.c_str(), (char *)NULL, envp); 43 | /* execle never returns when everything went well, so we necessarily encountered an error. */ 44 | LOG_ERROR(runscript_logger, RUNSCRIPT_EXEC_FAILED).arg(strerror(errno)); 45 | /* This only exits the child, not Kea itself. */ 46 | exit(EXIT_FAILURE); 47 | } else { 48 | /* Parent process */ 49 | if (script_wait) { 50 | LOG_DEBUG(runscript_logger, 50, RUNSCRIPT_WAITING_SCRIPT); 51 | ret = wait(&wstatus); 52 | if (ret == -1) { 53 | LOG_ERROR(runscript_logger, RUNSCRIPT_WAITPID_FAILED).arg(strerror(errno)); 54 | return -1; 55 | } 56 | /* Get exit code */ 57 | if (WIFEXITED(wstatus)) 58 | exitcode = WEXITSTATUS(wstatus); 59 | else 60 | /* By default, assume everything worked well */ 61 | exitcode = 0; 62 | return exitcode; 63 | } else { 64 | return 0; 65 | } 66 | } 67 | } 68 | 69 | } // end extern "C" 70 | -------------------------------------------------------------------------------- /src/runscript.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2017-2019 by Baptiste Jonglez 2 | * 3 | * This Source Code Form is subject to the terms of the Mozilla Public 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this 5 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | */ 7 | 8 | #include 9 | #include 10 | 11 | extern "C" { 12 | 13 | /* Runs the configured script with the given (single) argument and 14 | * environment variables. Returns -1 upon failure, or the exit code of 15 | * the script upon success. */ 16 | int run_script(std::string arg0, std::vector env); 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/version.cc: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2017-2019 by Baptiste Jonglez 2 | * 3 | * This Source Code Form is subject to the terms of the Mozilla Public 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this 5 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | */ 7 | 8 | // version.cc 9 | #include 10 | extern "C" { 11 | int version() { 12 | return (KEA_HOOKS_VERSION); 13 | } 14 | } 15 | --------------------------------------------------------------------------------