├── .envrc.sample ├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── cmd ├── gtptools │ ├── client.go │ ├── main.go │ └── server.go └── vinbero │ └── main.go ├── design ├── iso.png ├── logo-100.jpg ├── logo-w.png ├── logo.ai ├── logo.png ├── prime.png ├── prime@2x.png ├── prime@4x.png ├── readme.md ├── sub1.png ├── sub1@2x.png ├── sub1@4x.png ├── sub2.png ├── sub2@2x.png ├── sub2@4x.png ├── sub3.png ├── sub3@2x.png ├── sub3@4x.png ├── typo.ai └── var.png ├── docs ├── examples │ ├── l3vpnv4.md │ └── mobilev4.md ├── images │ ├── measurement_topology.png │ ├── mobile_v4_topology.png │ └── v4_l3vpn_topology.png ├── performance │ ├── graph │ │ ├── dx4_graph.py │ │ ├── encap_graph.py │ │ ├── end_graph.py │ │ ├── images │ │ │ ├── dx4_multi_flow.pdf │ │ │ ├── dx4_multi_flow.png │ │ │ ├── dx4_single_flow.pdf │ │ │ ├── dx4_single_flow.png │ │ │ ├── encap_multi_flow.pdf │ │ │ ├── encap_multi_flow.png │ │ │ ├── encap_single_flow.pdf │ │ │ ├── encap_single_flow.png │ │ │ ├── end_multi_flow.pdf │ │ │ ├── end_multi_flow.png │ │ │ ├── end_single_flow.pdf │ │ │ ├── end_single_flow.png │ │ │ ├── mobile_v4_multi_flow.png │ │ │ └── mobile_v4_single_flow.png │ │ └── mobile_v4_graph.py │ ├── performance.md │ ├── router.sh │ ├── srv6_end_dx4_for_bench.py │ ├── srv6_end_dx4_for_range_clients.py │ ├── srv6_end_for_bench.py │ ├── srv6_end_for_range_clients.py │ ├── srv6_end_m_gtp4_e_for_bench.py │ ├── srv6_end_m_gtp4_e_for_range_clients.py │ ├── srv6_hmgtp4d_for_bench.py │ ├── srv6_hmgtp4d_for_range_clients.py │ ├── srv6_t_encap_for_bench.py │ ├── srv6_t_encap_for_range_clients.py │ ├── trafficgen.sh │ └── trex_cfg.yaml └── tips.md ├── gen_bpf_helper.sh ├── go.mod ├── go.sum ├── include ├── bpf_endian.h ├── bpf_helper_defs.h └── bpf_helpers.h ├── internal ├── app.go └── load.go ├── pkg ├── config │ ├── config.go │ └── validation.go ├── coreelf │ └── elf.go ├── srv6 │ ├── consts.go │ ├── function.go │ ├── maps.go │ ├── seg6.go │ ├── seg6_local.go │ ├── transit_tablev4.go │ ├── transit_tablev6.go │ ├── tx_port.go │ └── v6addrheep.go ├── utils │ ├── array.go │ └── file.go ├── version │ └── version.go └── xdptool │ ├── apply.go │ ├── map.go │ └── possible_cpus.go ├── scripts ├── README.md ├── Vagrantfile ├── ansible.cfg ├── create-namespaces.sh ├── dev_setup.sh ├── env_setup.sh ├── inventry_handler.py ├── main.tf ├── setup.yml ├── setup_systemd │ ├── host-01.sh │ ├── host-02.sh │ ├── netsetup.service │ ├── router-01.sh │ ├── router-02.sh │ └── router-03.sh └── var.sample.yml ├── src ├── srv6.c ├── srv6_consts.h ├── srv6_helpers.h ├── srv6_maps.h └── srv6_structs.h ├── vinbero.yml └── vinbero.yml.sample /.envrc.sample: -------------------------------------------------------------------------------- 1 | export SAKURACLOUD_ACCESS_TOKEN=hoge 2 | export SAKURACLOUD_ACCESS_TOKEN_SECRET=piyo 3 | export SAKURACLOUD_ZONE=is1b 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/go,linux,macos,python,vagrant 3 | # Edit at https://www.gitignore.io/?templates=go,linux,macos,python,vagrant 4 | 5 | ### Go ### 6 | # Binaries for programs and plugins 7 | *.exe 8 | *.exe~ 9 | *.dll 10 | *.so 11 | *.dylib 12 | 13 | # Test binary, built with `go test -c` 14 | *.test 15 | 16 | # Output of the go coverage tool, specifically when used with LiteIDE 17 | *.out 18 | 19 | # Dependency directories (remove the comment below to include it) 20 | # vendor/ 21 | 22 | ### Go Patch ### 23 | /vendor/ 24 | /Godeps/ 25 | 26 | ### Linux ### 27 | *~ 28 | 29 | # temporary files which can be created if a process still has a handle open of a deleted file 30 | .fuse_hidden* 31 | 32 | # KDE directory preferences 33 | .directory 34 | 35 | # Linux trash folder which might appear on any partition or disk 36 | .Trash-* 37 | 38 | # .nfs files are created when an open file is removed but is still being accessed 39 | .nfs* 40 | 41 | ### macOS ### 42 | # General 43 | .DS_Store 44 | .AppleDouble 45 | .LSOverride 46 | 47 | # Icon must end with two \r 48 | Icon 49 | 50 | # Thumbnails 51 | ._* 52 | 53 | # Files that might appear in the root of a volume 54 | .DocumentRevisions-V100 55 | .fseventsd 56 | .Spotlight-V100 57 | .TemporaryItems 58 | .Trashes 59 | .VolumeIcon.icns 60 | .com.apple.timemachine.donotpresent 61 | 62 | # Directories potentially created on remote AFP share 63 | .AppleDB 64 | .AppleDesktop 65 | Network Trash Folder 66 | Temporary Items 67 | .apdisk 68 | 69 | ### Python ### 70 | # Byte-compiled / optimized / DLL files 71 | __pycache__/ 72 | *.py[cod] 73 | *$py.class 74 | 75 | # C extensions 76 | 77 | # Distribution / packaging 78 | .Python 79 | build/ 80 | develop-eggs/ 81 | dist/ 82 | downloads/ 83 | eggs/ 84 | .eggs/ 85 | lib/ 86 | lib64/ 87 | parts/ 88 | sdist/ 89 | var/ 90 | wheels/ 91 | pip-wheel-metadata/ 92 | share/python-wheels/ 93 | *.egg-info/ 94 | .installed.cfg 95 | *.egg 96 | MANIFEST 97 | 98 | # PyInstaller 99 | # Usually these files are written by a python script from a template 100 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 101 | *.manifest 102 | *.spec 103 | 104 | # Installer logs 105 | pip-log.txt 106 | pip-delete-this-directory.txt 107 | 108 | # Unit test / coverage reports 109 | htmlcov/ 110 | .tox/ 111 | .nox/ 112 | .coverage 113 | .coverage.* 114 | .cache 115 | nosetests.xml 116 | coverage.xml 117 | *.cover 118 | .hypothesis/ 119 | .pytest_cache/ 120 | 121 | # Translations 122 | *.mo 123 | *.pot 124 | 125 | # Scrapy stuff: 126 | .scrapy 127 | 128 | # Sphinx documentation 129 | docs/_build/ 130 | 131 | # PyBuilder 132 | target/ 133 | 134 | # pyenv 135 | .python-version 136 | 137 | # pipenv 138 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 139 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 140 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 141 | # install all needed dependencies. 142 | #Pipfile.lock 143 | 144 | # celery beat schedule file 145 | celerybeat-schedule 146 | 147 | # SageMath parsed files 148 | *.sage.py 149 | 150 | # Spyder project settings 151 | .spyderproject 152 | .spyproject 153 | 154 | # Rope project settings 155 | .ropeproject 156 | 157 | # Mr Developer 158 | .mr.developer.cfg 159 | .project 160 | .pydevproject 161 | 162 | # mkdocs documentation 163 | /site 164 | 165 | # mypy 166 | .mypy_cache/ 167 | .dmypy.json 168 | dmypy.json 169 | 170 | # Pyre type checker 171 | .pyre/ 172 | 173 | ### Vagrant ### 174 | # General 175 | .vagrant 176 | 177 | # Log files (if you are creating logs in debug mode, uncomment this) 178 | # *.log 179 | 180 | ### Vagrant Patch ### 181 | *.box 182 | 183 | # End of https://www.gitignore.io/api/go,linux,macos,python,vagrant 184 | 185 | ## dev env not include file 186 | .envrc 187 | .vscode/settings.json 188 | obj/srv6.o 189 | build 190 | bin 191 | .terraform 192 | terraform.tfstate 193 | .terraform.* 194 | terraform.tfstate.backup 195 | id_rsa 196 | var.yml 197 | hook.h 198 | main 199 | srv6_bpf.* 200 | linux-* 201 | *.pcap 202 | objdump 203 | *.log 204 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | NAME := vinbero 2 | 3 | #brunch name version 4 | VERSION := $(shell git rev-parse --abbrev-ref HEAD) 5 | 6 | PKG_NAME=$(shell basename `pwd`) 7 | 8 | LDFLAGS := -ldflags="-s -w -X \"github.com/takehaya/vinbero/pkg/version.Version=$(VERSION)\" -extldflags \"-static\"" 9 | SRCS := $(shell find . -type f -name '*.go') 10 | 11 | .DEFAULT_GOAL := build 12 | build: $(SRCS) gen 13 | go build $(LDFLAGS) -o ./bin/$(NAME) ./cmd/$(NAME) 14 | 15 | .PHONY: gtp 16 | gtp: 17 | go build $(LDFLAGS) -o ./bin/gtptools ./cmd/gtptools 18 | 19 | .PHONY: run 20 | run: 21 | go run $(LDFLAGS) ./cmd/$(NAME) 22 | 23 | .PHONY: test 24 | test: 25 | go test -v ./integration 26 | 27 | ## lint 28 | .PHONY: lint 29 | lint: 30 | @for pkg in $$(go list ./...): do \ 31 | golint --set_exit_status $$pkg || exit $$?; \ 32 | done 33 | 34 | .PHONY: codecheck 35 | codecheck: 36 | test -z "$(gofmt -s -l . | tee /dev/stderr)" 37 | go vet ./... 38 | 39 | .PHONY: clean 40 | clean: 41 | rm -rf bin 42 | 43 | .PHONY: install 44 | install: 45 | go install $(LDFLAGS) ./cmd/$(NAME) 46 | 47 | .PHONY: gen 48 | gen: 49 | go generate pkg/coreelf/elf.go 50 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Vinbero 2 | Vinbero is a very fast high-performance SRv6 implementation 3 | Vibero is about 3-5 Mpps in single flow, which is 2-3 times better performance than linux! 4 | 5 |
6 | 7 |
8 | 9 | The goal of this project is to implement the basic functions of SRv6 so that users can enjoy network programming:) 10 | For example, L2,L3VPN, ServiceChain, Mobile Uplane(in UPF)...etc, 11 | 12 | This implementation is also very powerful because it is written in XDP. 13 | It does not require any special equipment like P4. 14 | All you need is the latest linux kernel to run it. 15 | It is also a very small implementation compared to VPP. 16 | It's easy to understand and easy to add functionality.It is also possible to incorporate these:)(e.g. CNI, NOS...etc) 17 | 18 | Please raise issue with use case description if you want to any SRv6 functions not implemented yet or let's make a contribution! 19 | 20 | ## Required package 21 | * linux: 5.8<=x 22 | * It means that the version you are testing is 5.8 or higher. Actually, if BTF supports it, I think there is no problem. 23 | * golang: 1.3<=x 24 | * clang: 9<=x 25 | 26 | ## Setup 27 | ``` 28 | ulimit -l unlimited 29 | 30 | # if "ulimit -l unlimited" is not working when plz check 31 | echo "DefaultLimitMEMLOCK=infinity">>/etc/systemd/system.conf 32 | echo "DefaultLimitMEMLOCK=infinity">>/etc/systemd/user.conf 33 | ``` 34 | 35 | remove offload 36 | ``` 37 | for i in rx tx tso ufo gso gro lro tx nocache copy sg txvlan rxvlan; do 38 | /sbin/ethtool -K eth1 $i off 2>&1 > /dev/null; 39 | done 40 | ``` 41 | 42 | use irqaffinity. this mellanox nic case. 43 | ``` 44 | git clone https://github.com/Mellanox/mlnx-tools.git 45 | cd mlnx-tools/ 46 | ./set_irq_affinity.sh ens4f0np0 47 | ``` 48 | 49 | 50 | ## Build 51 | ``` 52 | cd include 53 | wget https://raw.githubusercontent.com/cloudflare/xdpcap/master/hook.h 54 | cd .. 55 | make 56 | ``` 57 | ## ConfigExample 58 | This is an example of performing the End operation. 59 | ```yaml 60 | internal: 61 | devices: 62 | - eth1 63 | - eth2 64 | settings: 65 | functions: 66 | - action: SEG6_LOCAL_ACTION_END 67 | triggerAddr: fc00:2::1/128 68 | actionSrcAddr: fc00:1::1 69 | flaver: NONE 70 | - action: SEG6_LOCAL_ACTION_END 71 | triggerAddr: fc00:2::2/128 72 | actionSrcAddr: fc00:1::1 73 | flaver: NONE 74 | ``` 75 | 76 | In the transit case, actionSrcAddr works as a so-called bsid. 77 | 78 | See this for details: [vinbero.yml.sample](./vinbero.yml.sample) 79 | 80 | ## Run 81 | ``` 82 | ./bin/vinbero 83 | ``` 84 | 85 | ## List of SRv6 functions of interest and status (a.k.a. Road Map) 86 | 87 | ### Reference list 88 | * [draft-filsfils-spring-srv6-network-programming](https://datatracker.ietf.org/doc/draft-ietf-spring-srv6-network-programming/) 89 | * [draft-ietf-dmm-srv6-mobile-uplane](https://datatracker.ietf.org/doc/draft-ietf-dmm-srv6-mobile-uplane/) 90 | * [draft-murakami-dmm-user-plane-message-encoding](https://datatracker.ietf.org/doc/draft-murakami-dmm-user-plane-message-encoding) 91 | 92 | ### Transit behaviors 93 | 94 | | Function | schedule | description | 95 | |----------|----------|-------------| 96 | | T | n/a | Transit behavior| 97 | | T.Insert | future | | 98 | | T.Insert.Red | | | 99 | | T.Encaps | Done | | 100 | | T.Encaps.Red | | | 101 | | T.Encaps.L2 | future | | 102 | | T.Encaps.L2.Red | | | 103 | 104 | ### Functions associated with a SID 105 | 106 | | Function | schedule | description | 107 | |----------|----------|-------------| 108 | | End | Done | | 109 | | End.X | Fed, 2020 | | 110 | | End.T | | | 111 | | End.DX2 (V) | | | 112 | | End.DT2 (U/M) | | | 113 | | End.DX6 | Done | | 114 | | End.DX4 | Done | | 115 | | End.DT6 | | | 116 | | End.DT4 | | | 117 | | End.DT46 | | | 118 | | End.B6.Insert | | | 119 | | End.B6.Insert.Red | | | 120 | | End.B6.Encaps | | | 121 | | End.B6.Encaps.Red | | | 122 | | End.BM | | | 123 | | End.S | | | 124 | | Args.Mob.Session | Done | Consider with End.MAP, End.DT and End.DX | 125 | | End.MAP | | | 126 | | End.M.GTP6.D | Mar, 2021 | GTP-U/IPv6 => SRv6, For implementation purposes, it is treated as transit | 127 | | End.M.GTP6.D.Di | Mar, 2021 | GTP-U/IPv6 => SRv6, For implementation purposes, it is treated as transit | 128 | | End.M.GTP6.E | Mar, 2021 | SRv6 => GTP-U/IPv6 | 129 | | End.M.GTP4.E | partial Done, Mar, 2021 | SRv6 => GTP-U/IPv4 gtpv1ext hdr is not supported. | 130 | | H.M.GTP4.D | partial Done, Mar, 2021 | GTP-U/IPv4 => SRv6, Currently, gtpv1ext hdr is not supported. | 131 | | End.Limit | | Rate Limiting function | 132 | 133 | ### Non functional design items 134 | 135 | | Item name | schedule | 136 | |-----------|----------| 137 | | BSID friendly table structure | future | 138 | 139 | ### Flavours 140 | 141 | | Function | schedule | description | 142 | |----------|----------|-------------| 143 | | PSP | future | Penultimate Segment Pop | 144 | | USP | | Ultimate Segment Pop | 145 | | USD | | Ultimate Segment Decapsulation | 146 | 147 | ## Respectful implementation 148 | I'm using these as a reference. thanks:) 149 | * [p4srv6](https://github.com/ebiken/p4srv6) 150 | * [linux/samples/bpf](https://github.com/torvalds/linux/tree/master/samples/bpf) 151 | * [VPP/srv6_mobile_plugin_doc](https://docs.fd.io/vpp/20.05/d7/d3c/srv6_mobile_plugin_doc.html) 152 | 153 | ## Trivia 154 | The Vinbero is an Esperanto word meaning `grape` 155 | A meshed node running SRv6 looks like a grape when viewed from above:) 156 | -------------------------------------------------------------------------------- /cmd/gtptools/client.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "net" 7 | "os" 8 | "os/signal" 9 | "strconv" 10 | "syscall" 11 | 12 | "github.com/pkg/errors" 13 | "github.com/wmnsk/go-gtp/gtpv1" 14 | ) 15 | 16 | func client(listen, peer, subscriber, ote, ite string) error { 17 | addr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%v:2152", listen)) 18 | if err != nil { 19 | return errors.WithStack(err) 20 | } 21 | raddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("$v:2152", peer)) 22 | if err != nil { 23 | return errors.WithStack(err) 24 | } 25 | ctx, cancel := context.WithCancel(context.Background()) 26 | defer cancel() 27 | 28 | uConn, err := gtpv1.DialUPlane(ctx, addr, raddr) 29 | defer uConn.Close() 30 | 31 | if err := uConn.EnableKernelGTP("gtp0", gtpv1.RoleSGSN); err != nil { 32 | return errors.WithStack(err) 33 | } 34 | oteint, err := strconv.Atoi(ote) 35 | if err != nil { 36 | return errors.WithStack(err) 37 | } 38 | iteint, err := strconv.Atoi(ite) 39 | if err != nil { 40 | return errors.WithStack(err) 41 | } 42 | 43 | if err = uConn.AddTunnelOverride( 44 | net.ParseIP(peer), // GTP peer's IP 45 | net.ParseIP(subscriber), // subscriber's IP 46 | uint32(oteint), // outgoing TEID 47 | uint32(iteint), // incoming TEID 48 | ); err != nil { 49 | return errors.WithStack(err) 50 | } 51 | 52 | signalChan := make(chan os.Signal, 1) 53 | signal.Notify(signalChan, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT) 54 | 55 | for { 56 | select { 57 | case <-signalChan: 58 | fmt.Println("Ctl-C exit") 59 | // delete a tunnel by giving an incoming TEID. 60 | if err = uConn.DelTunnelByITEI(uint32(iteint)); err != nil { 61 | return errors.WithStack(err) 62 | } 63 | 64 | // delete a tunnel by giving an IP address assigned to a subscriber. 65 | if err = uConn.DelTunnelByMSAddress(net.ParseIP(subscriber)); err != nil { 66 | return errors.WithStack(err) 67 | } 68 | return nil 69 | } 70 | } 71 | return nil 72 | } 73 | -------------------------------------------------------------------------------- /cmd/gtptools/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "os" 7 | 8 | "github.com/pkg/errors" 9 | "github.com/takehaya/vinbero/pkg/version" 10 | "github.com/urfave/cli" 11 | ) 12 | 13 | func main() { 14 | app := newApp(version.Version) 15 | if err := app.Run(os.Args); err != nil { 16 | log.Fatalf("%+v", err) 17 | } 18 | } 19 | 20 | func newApp(version string) *cli.App { 21 | app := cli.NewApp() 22 | app.Name = "gtptools" 23 | app.Version = version 24 | 25 | app.Usage = "Implementation to spit out only gtpv1-u" 26 | 27 | app.EnableBashCompletion = true 28 | app.Flags = []cli.Flag{ 29 | cli.StringFlag{ 30 | Name: "mode", 31 | Value: "server", 32 | Usage: "mode", 33 | }, 34 | cli.StringFlag{ 35 | Name: "listen", 36 | Value: "172.0.1.1", 37 | Usage: "service listen addr", 38 | }, 39 | cli.StringFlag{ 40 | Name: "peer", 41 | Value: "172.0.2.1", 42 | Usage: "remote peer addr", 43 | }, 44 | cli.StringFlag{ 45 | Name: "subscriber", 46 | Value: "1.0.1.2", 47 | Usage: "mobile station addr", 48 | }, 49 | cli.StringFlag{ 50 | Name: "ote", 51 | Value: "100", 52 | Usage: "putput TEID", 53 | }, 54 | cli.StringFlag{ 55 | Name: "ite", 56 | Value: "200", 57 | Usage: "input TEID", 58 | }, 59 | } 60 | app.Action = run 61 | return app 62 | } 63 | 64 | func run(ctx *cli.Context) (err error) { 65 | mode := ctx.String("mode") 66 | peer := ctx.String("peer") 67 | subscriber := ctx.String("subscriber") 68 | ote := ctx.String("ote") 69 | ite := ctx.String("ite") 70 | listen := ctx.String("listen") 71 | 72 | if mode == "server" { 73 | err = server(listen, peer, subscriber, ote, ite) 74 | 75 | } else if mode == "client" { 76 | err = client(listen, peer, subscriber, ote, ite) 77 | } else { 78 | return errors.Errorf("%v is inv mode", mode) 79 | } 80 | if err != nil { 81 | return err 82 | } 83 | fmt.Println("exit") 84 | return nil 85 | } 86 | -------------------------------------------------------------------------------- /cmd/gtptools/server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "net" 7 | "os" 8 | "os/signal" 9 | "strconv" 10 | "syscall" 11 | 12 | "github.com/pkg/errors" 13 | 14 | "github.com/wmnsk/go-gtp/gtpv1" 15 | ) 16 | 17 | func server(listen, peer, subscriber, ote, ite string) error { 18 | addr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%v:2152", listen)) 19 | if err != nil { 20 | return errors.WithStack(err) 21 | } 22 | ctx, cancel := context.WithCancel(context.Background()) 23 | defer cancel() 24 | 25 | uConn := gtpv1.NewUPlaneConn(addr) 26 | defer uConn.Close() 27 | 28 | if err = uConn.ListenAndServe(ctx); err != nil { 29 | return errors.WithStack(err) 30 | } 31 | 32 | if err := uConn.EnableKernelGTP("gtp0", gtpv1.RoleSGSN); err != nil { 33 | return errors.WithStack(err) 34 | } 35 | oteint, err := strconv.Atoi(ote) 36 | if err != nil { 37 | return errors.WithStack(err) 38 | } 39 | iteint, err := strconv.Atoi(ite) 40 | if err != nil { 41 | return errors.WithStack(err) 42 | } 43 | 44 | if err = uConn.AddTunnelOverride( 45 | net.ParseIP(peer), // GTP peer's IP 46 | net.ParseIP(subscriber), // subscriber's IP 47 | uint32(oteint), // outgoing TEID 48 | uint32(iteint), // incoming TEID 49 | ); err != nil { 50 | return errors.WithStack(err) 51 | } 52 | 53 | signalChan := make(chan os.Signal, 1) 54 | signal.Notify(signalChan, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT) 55 | 56 | for { 57 | select { 58 | case <-signalChan: 59 | fmt.Println("Ctl-C exit") 60 | // delete a tunnel by giving an incoming TEID. 61 | if err = uConn.DelTunnelByITEI(uint32(iteint)); err != nil { 62 | return errors.WithStack(err) 63 | } 64 | 65 | // delete a tunnel by giving an IP address assigned to a subscriber. 66 | if err = uConn.DelTunnelByMSAddress(net.ParseIP(subscriber)); err != nil { 67 | return errors.WithStack(err) 68 | } 69 | return nil 70 | } 71 | } 72 | return nil 73 | } 74 | -------------------------------------------------------------------------------- /cmd/vinbero/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "os" 7 | 8 | "github.com/pkg/errors" 9 | "github.com/takehaya/vinbero/internal" 10 | "github.com/takehaya/vinbero/pkg/config" 11 | "github.com/takehaya/vinbero/pkg/utils" 12 | "github.com/takehaya/vinbero/pkg/version" 13 | "github.com/urfave/cli" 14 | ) 15 | 16 | func main() { 17 | app := newApp(version.Version) 18 | if err := app.Run(os.Args); err != nil { 19 | log.Fatalf("%+v", err) 20 | } 21 | } 22 | 23 | func newApp(version string) *cli.App { 24 | app := cli.NewApp() 25 | app.Name = "Vinbero" 26 | app.Version = version 27 | 28 | app.Usage = "Vinbero in SRv6 Function Subset" 29 | 30 | app.EnableBashCompletion = true 31 | app.Flags = []cli.Flag{ 32 | cli.StringFlag{ 33 | Name: "configfile", 34 | Value: "./vinbero.yml", 35 | Usage: "config file path", 36 | }, 37 | } 38 | app.Action = run 39 | return app 40 | } 41 | 42 | func run(ctx *cli.Context) error { 43 | configfile := ctx.String("configfile") 44 | 45 | if !utils.FileExists(configfile) { 46 | return errors.WithStack(fmt.Errorf("configfile file not found: %s", configfile)) 47 | } 48 | c, err := config.LoadFile(configfile) 49 | if err != nil { 50 | return errors.WithStack(err) 51 | } 52 | 53 | err = c.Validate() 54 | if err != nil { 55 | return errors.WithStack(err) 56 | } 57 | err = internal.App(c) 58 | if err != nil { 59 | return errors.WithStack(err) 60 | } 61 | 62 | return nil 63 | } 64 | -------------------------------------------------------------------------------- /design/iso.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takehaya/Vinbero/e35cfdc1b06b083d5a7aae53e1ba99e65027ec15/design/iso.png -------------------------------------------------------------------------------- /design/logo-100.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takehaya/Vinbero/e35cfdc1b06b083d5a7aae53e1ba99e65027ec15/design/logo-100.jpg -------------------------------------------------------------------------------- /design/logo-w.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takehaya/Vinbero/e35cfdc1b06b083d5a7aae53e1ba99e65027ec15/design/logo-w.png -------------------------------------------------------------------------------- /design/logo.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takehaya/Vinbero/e35cfdc1b06b083d5a7aae53e1ba99e65027ec15/design/logo.ai -------------------------------------------------------------------------------- /design/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takehaya/Vinbero/e35cfdc1b06b083d5a7aae53e1ba99e65027ec15/design/logo.png -------------------------------------------------------------------------------- /design/prime.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takehaya/Vinbero/e35cfdc1b06b083d5a7aae53e1ba99e65027ec15/design/prime.png -------------------------------------------------------------------------------- /design/prime@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takehaya/Vinbero/e35cfdc1b06b083d5a7aae53e1ba99e65027ec15/design/prime@2x.png -------------------------------------------------------------------------------- /design/prime@4x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takehaya/Vinbero/e35cfdc1b06b083d5a7aae53e1ba99e65027ec15/design/prime@4x.png -------------------------------------------------------------------------------- /design/readme.md: -------------------------------------------------------------------------------- 1 | # logo 2 | 3 |
4 | 5 |
6 | 7 | ## isolation 8 | ![iso](./iso.png) 9 | 10 | ## color variation 11 | ![var](./var.png) 12 | 13 | ### prime 14 | `#BEDCFA`,`#98ACF8`,`#B088F9`,`#DA9FF9` 15 | 16 | ### sub1 17 | `#D7FBE8`,`#9CF4C4`,`#63D2A2`,`#1EAC89` 18 | 19 | ### sub2 20 | `#9AD3BC`,`#F2EAC2`,`#F5B462`,`#ED524B` 21 | 22 | ### sub3 23 | `#1B262C`,`#0D4D75`,`#3282B8`,`#BAE1FA` 24 | -------------------------------------------------------------------------------- /design/sub1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takehaya/Vinbero/e35cfdc1b06b083d5a7aae53e1ba99e65027ec15/design/sub1.png -------------------------------------------------------------------------------- /design/sub1@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takehaya/Vinbero/e35cfdc1b06b083d5a7aae53e1ba99e65027ec15/design/sub1@2x.png -------------------------------------------------------------------------------- /design/sub1@4x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takehaya/Vinbero/e35cfdc1b06b083d5a7aae53e1ba99e65027ec15/design/sub1@4x.png -------------------------------------------------------------------------------- /design/sub2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takehaya/Vinbero/e35cfdc1b06b083d5a7aae53e1ba99e65027ec15/design/sub2.png -------------------------------------------------------------------------------- /design/sub2@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takehaya/Vinbero/e35cfdc1b06b083d5a7aae53e1ba99e65027ec15/design/sub2@2x.png -------------------------------------------------------------------------------- /design/sub2@4x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takehaya/Vinbero/e35cfdc1b06b083d5a7aae53e1ba99e65027ec15/design/sub2@4x.png -------------------------------------------------------------------------------- /design/sub3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takehaya/Vinbero/e35cfdc1b06b083d5a7aae53e1ba99e65027ec15/design/sub3.png -------------------------------------------------------------------------------- /design/sub3@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takehaya/Vinbero/e35cfdc1b06b083d5a7aae53e1ba99e65027ec15/design/sub3@2x.png -------------------------------------------------------------------------------- /design/sub3@4x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takehaya/Vinbero/e35cfdc1b06b083d5a7aae53e1ba99e65027ec15/design/sub3@4x.png -------------------------------------------------------------------------------- /design/typo.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takehaya/Vinbero/e35cfdc1b06b083d5a7aae53e1ba99e65027ec15/design/typo.ai -------------------------------------------------------------------------------- /design/var.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takehaya/Vinbero/e35cfdc1b06b083d5a7aae53e1ba99e65027ec15/design/var.png -------------------------------------------------------------------------------- /docs/examples/l3vpnv4.md: -------------------------------------------------------------------------------- 1 | # IPv4 L3VPN 2 | 3 | ## Subset 4 | * T.Encap 5 | * End.DX4 6 | * End 7 | 8 | ## Topology 9 | ![](../images/v4_l3vpn_topology.png) 10 | 11 | ## Config 12 | * gw1 13 | ```yaml 14 | internal: 15 | # logfile: "/var/log/vinbero.log" 16 | logfile: "./vinbero.log" 17 | development: false 18 | devices: 19 | - eth1 20 | - eth2 21 | settings: 22 | functions: 23 | - action: SEG6_LOCAL_ACTION_END_DX4 24 | triggerAddr: fc00:1::1/128 25 | nexthop: 172.0.1.1 26 | transitv4: 27 | - action: SEG6_IPTUN_MODE_ENCAP 28 | triggerAddr: 172.0.2.0/24 29 | actionSrcAddr: fc00:1::1 30 | segments: 31 | - fc00:3::3 # last arrive next hop 32 | - fc00:2::1 33 | ``` 34 | 35 | * router 36 | ```yaml 37 | internal: 38 | logfile: "./vinbero.log" 39 | development: false 40 | devices: 41 | - eth1 42 | - eth2 43 | settings: 44 | functions: 45 | - action: SEG6_LOCAL_ACTION_END 46 | addr: fc00:2::1/128 47 | - action: SEG6_LOCAL_ACTION_END 48 | addr: fc00:2::2/128 49 | ``` 50 | 51 | * gw1 52 | ```yaml 53 | internal: 54 | logfile: "./vinbero.log" 55 | development: false 56 | devices: 57 | - eth1 58 | - eth2 59 | settings: 60 | functions: 61 | - action: SEG6_LOCAL_ACTION_END_M_GTP4_E 62 | triggerAddr: fc00:3::/48 63 | actionSrcAddr: 172.0.2.2 64 | v4AddrSPos: 64 65 | v4AddrDPos: 48 66 | transitv4: 67 | - action: SEG6_IPTUN_MODE_ENCAP_H_M_GTP4_D 68 | triggerAddr: 172.0.1.0/24 69 | actionSrcAddr: fc00:3::/64 70 | actionDstAddr: fc00:1::/48 # last arrive next hop 71 | segments: 72 | - fc00:2::2 73 | ``` 74 | -------------------------------------------------------------------------------- /docs/examples/mobilev4.md: -------------------------------------------------------------------------------- 1 | # SRv6/GTPv1 IPv4 Stateless Translation Function 2 | 3 | ## Subset 4 | * H.M.GTP4.D 5 | * End.M.GTP4.E 6 | * End 7 | 8 | ## Topology 9 | ![](../images/mobile_v4_topology.png) 10 | 11 | ## GTPv1 Tuunel Setup 12 | ```bash 13 | # host1 14 | > Create gtp device 15 | # if failed modprobe, plz new kernel header and module 16 | modprobe udp_tunnel 17 | modprobe ip6_udp_tunnel 18 | modprobe gtp 19 | ./gtp-link add gtp1 20 | 21 | > Open a new console and configure tunnel (PDP session) 22 | ./gtp-tunnel add gtp1 v1 200 100 10.0.1.2 172.0.2.1 23 | ip route add 10.0.1.2/32 dev gtp1 24 | ip link set gtp1 mtu 1500 25 | 26 | # host2 27 | > Create gtp device 28 | modprobe udp_tunnel 29 | modprobe ip6_udp_tunnel 30 | modprobe gtp 31 | ./gtp-link add gtp2 32 | 33 | > Open a new console and configure tunnel (PDP session) 34 | ./gtp-tunnel add gtp2 v1 100 200 10.0.1.1 172.0.1.1 35 | ip route add 10.0.1.1/32 dev gtp2 36 | ip link set gtp2 mtu 1500 37 | ``` 38 | 39 | ## Config 40 | * gw1 41 | ```yaml 42 | internal: 43 | logfile: "./vinbero.log" 44 | development: false 45 | devices: 46 | - eth1 47 | - eth2 48 | settings: 49 | functions: 50 | - action: SEG6_LOCAL_ACTION_END_M_GTP4_E 51 | triggerAddr: fc00:1::/48 52 | actionSrcAddr: 172.0.1.2 53 | v4AddrSPos: 64 54 | v4AddrDPos: 48 55 | transitv4: 56 | - action: SEG6_IPTUN_MODE_ENCAP_H_M_GTP4_D 57 | triggerAddr: 172.0.2.0/24 58 | actionSrcAddr: fc00:1::/64 59 | actionDstAddr: fc00:3::/48 # last arrive next hop 60 | segments: 61 | - fc00:2::1 62 | ``` 63 | 64 | * router 65 | ```yaml 66 | internal: 67 | logfile: "./vinbero.log" 68 | development: false 69 | devices: 70 | - eth1 71 | - eth2 72 | settings: 73 | functions: 74 | - action: SEG6_LOCAL_ACTION_END 75 | addr: fc00:2::1/128 76 | - action: SEG6_LOCAL_ACTION_END 77 | addr: fc00:2::2/128 78 | ``` 79 | 80 | * gw1 81 | ```yaml 82 | internal: 83 | logfile: "./vinbero.log" 84 | development: false 85 | devices: 86 | - eth1 87 | - eth2 88 | settings: 89 | functions: 90 | - action: SEG6_LOCAL_ACTION_END_M_GTP4_E 91 | triggerAddr: fc00:3::/48 92 | actionSrcAddr: 172.0.2.2 93 | v4AddrSPos: 64 94 | v4AddrDPos: 48 95 | transitv4: 96 | - action: SEG6_IPTUN_MODE_ENCAP_H_M_GTP4_D 97 | triggerAddr: 172.0.1.0/24 98 | actionSrcAddr: fc00:3::/64 99 | actionDstAddr: fc00:1::/48 # last arrive next hop 100 | segments: 101 | - fc00:2::2 102 | ``` 103 | -------------------------------------------------------------------------------- /docs/images/measurement_topology.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takehaya/Vinbero/e35cfdc1b06b083d5a7aae53e1ba99e65027ec15/docs/images/measurement_topology.png -------------------------------------------------------------------------------- /docs/images/mobile_v4_topology.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takehaya/Vinbero/e35cfdc1b06b083d5a7aae53e1ba99e65027ec15/docs/images/mobile_v4_topology.png -------------------------------------------------------------------------------- /docs/images/v4_l3vpn_topology.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takehaya/Vinbero/e35cfdc1b06b083d5a7aae53e1ba99e65027ec15/docs/images/v4_l3vpn_topology.png -------------------------------------------------------------------------------- /docs/performance/graph/dx4_graph.py: -------------------------------------------------------------------------------- 1 | import seaborn as sns 2 | import matplotlib.pyplot as plt 3 | import numpy as np 4 | sns.set() 5 | 6 | name = "End.DX4" 7 | formatimage = "pdf" 8 | vinbero_single = [3.9845887999999996, 1.9922943999999998, 2.87834112, 3.29252864, 3.5913728, 3.4723181878651688] 9 | vinbero_multi = [3.9007027200000004, 1.9922943999999998, 2.9569843199999997, 3.34495744,3.6438016, 3.4402718202247193] 10 | linux_single = [1.7496473600000002, 0.85302272, 1.30023424, 1.51781376, 1.62922496, 1.6607558759550562] 11 | linux_multi = [1.69555968, 0.8663449599999999, 1.31072, 1.5151923200000001, 1.62791424, 1.659813335730337] 12 | 13 | left = np.arange(len(vinbero_single)) 14 | labels = [64, 128, 256, 512, 1024, 1424] 15 | width = 0.3 16 | 17 | plt.title(f"{name}/Payload pps (single flow)") 18 | plt.ylabel("Mpps") 19 | plt.xlabel("byte") 20 | 21 | plt.bar(left, vinbero_single, color='r', width=width, align='center', label="vinbero") 22 | plt.bar(left+width, linux_single, color='b', width=width, align='center', label="linux") 23 | 24 | plt.xticks(left + width/2, labels) 25 | plt.legend(loc="best") 26 | 27 | plt.savefig(f"./images/dx4_single_flow.{formatimage}") 28 | 29 | 30 | left = np.arange(len(vinbero_multi)) 31 | plt.title(f"{name}/Payload pps (multi flow)") 32 | plt.ylabel("Mpps") 33 | plt.xlabel("byte") 34 | 35 | plt.bar(left, vinbero_multi, color='r', width=width, align='center', label="vinbero") 36 | plt.bar(left+width, linux_multi, color='b', width=width, align='center', label="linux") 37 | 38 | plt.xticks(left + width/2, labels) 39 | plt.savefig(f"./images/dx4_multi_flow.{formatimage}") 40 | -------------------------------------------------------------------------------- /docs/performance/graph/encap_graph.py: -------------------------------------------------------------------------------- 1 | import seaborn as sns 2 | import matplotlib.pyplot as plt 3 | import numpy as np 4 | sns.set() 5 | 6 | formatimage = "pdf" 7 | name = "T.Encap" 8 | vinbero_single = [5.53648128, 4.1209036800000005, 3.47078656, 3.2007782400000004, 2.89275904, 2.807827329438202] 9 | vinbero_multi = [70.02390528000001, 35.148267520000005, 17.53219072, 8.80541696, 4.40008704, 3.1556246723595502] 10 | linux_single = [3.670016, 2.6633830400000003, 2.03423744, 1.8664652800000001, 1.53092096, 1.4524544862921347] 11 | linux_multi = [23.320330239999997, 11.660165119999998, 5.84056832, 1.81665792, 1.4562099199999998, 1.0471621896629213] 12 | 13 | left = np.arange(len(vinbero_single)) 14 | labels = [64, 128, 256, 512, 1024, 1424] 15 | width = 0.3 16 | 17 | plt.title(f"{name}/Payload pps (single flow)") 18 | plt.ylabel("Mpps") 19 | plt.xlabel("byte") 20 | 21 | plt.bar(left, vinbero_single, color='r', width=width, align='center', label="vinbero") 22 | plt.bar(left+width, linux_single, color='b', width=width, align='center', label="linux") 23 | 24 | plt.xticks(left + width/2, labels) 25 | plt.legend(loc="best") 26 | plt.savefig(f"./images/encap_single_flow.{formatimage}") 27 | 28 | 29 | left = np.arange(len(vinbero_multi)) 30 | plt.title(f"{name}/Payload pps (multi flow)") 31 | plt.ylabel("Mpps") 32 | plt.xlabel("byte") 33 | 34 | plt.bar(left, vinbero_multi, color='r', width=width, align='center', label="vinbero") 35 | plt.bar(left+width, linux_multi, color='b', width=width, align='center', label="linux") 36 | 37 | plt.xticks(left + width/2, labels) 38 | plt.savefig(f"./images/encap_multi_flow.{formatimage}") 39 | -------------------------------------------------------------------------------- /docs/performance/graph/end_graph.py: -------------------------------------------------------------------------------- 1 | import seaborn as sns 2 | import matplotlib.pyplot as plt 3 | import numpy as np 4 | sns.set() 5 | 6 | formatimage = "pdf" 7 | name = "End" 8 | vinbero_single = [7.73849088, 3.8587596800000004, 3.48127232, 3.4865152000000004, 3.49175808, 3.483628670561798] 9 | vinbero_multi = [7.61266176, 3.54418688, 3.4340864, 3.42884352, 3.43801856, 3.4327314984269663] 10 | linux_single = [3.64904448, 1.85597952, 1.82976512, 1.8664652800000001, 1.87826176, 1.8322981968539327] 11 | linux_multi = [3.71195904, 1.8874368000000001, 1.8507366399999998, 1.81665792, 1.85991168, 1.8134473923595504] 12 | 13 | left = np.arange(len(vinbero_single)) 14 | labels = [64, 128, 256, 512, 1024, 1424] 15 | width = 0.3 16 | 17 | plt.title(f"{name}/Payload pps (single flow)") 18 | plt.ylabel("Mpps") 19 | plt.xlabel("byte") 20 | 21 | plt.bar(left, vinbero_single, color='r', width=width, align='center', label="vinbero") 22 | plt.bar(left+width, linux_single, color='b', width=width, align='center', label="linux") 23 | 24 | plt.xticks(left + width/2, labels) 25 | plt.legend(loc="best") 26 | plt.savefig(f"./images/end_single_flow.{formatimage}") 27 | 28 | 29 | left = np.arange(len(vinbero_multi)) 30 | plt.title(f"{name}/Payload pps (multi flow)") 31 | plt.ylabel("Mpps") 32 | plt.xlabel("byte") 33 | 34 | plt.bar(left, vinbero_multi, color='r', width=width, align='center', label="vinbero") 35 | plt.bar(left+width, linux_multi, color='b', width=width, align='center', label="linux") 36 | 37 | plt.xticks(left + width/2, labels) 38 | plt.savefig(f"./images/end_multi_flow.{formatimage}") 39 | -------------------------------------------------------------------------------- /docs/performance/graph/images/dx4_multi_flow.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takehaya/Vinbero/e35cfdc1b06b083d5a7aae53e1ba99e65027ec15/docs/performance/graph/images/dx4_multi_flow.pdf -------------------------------------------------------------------------------- /docs/performance/graph/images/dx4_multi_flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takehaya/Vinbero/e35cfdc1b06b083d5a7aae53e1ba99e65027ec15/docs/performance/graph/images/dx4_multi_flow.png -------------------------------------------------------------------------------- /docs/performance/graph/images/dx4_single_flow.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takehaya/Vinbero/e35cfdc1b06b083d5a7aae53e1ba99e65027ec15/docs/performance/graph/images/dx4_single_flow.pdf -------------------------------------------------------------------------------- /docs/performance/graph/images/dx4_single_flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takehaya/Vinbero/e35cfdc1b06b083d5a7aae53e1ba99e65027ec15/docs/performance/graph/images/dx4_single_flow.png -------------------------------------------------------------------------------- /docs/performance/graph/images/encap_multi_flow.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takehaya/Vinbero/e35cfdc1b06b083d5a7aae53e1ba99e65027ec15/docs/performance/graph/images/encap_multi_flow.pdf -------------------------------------------------------------------------------- /docs/performance/graph/images/encap_multi_flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takehaya/Vinbero/e35cfdc1b06b083d5a7aae53e1ba99e65027ec15/docs/performance/graph/images/encap_multi_flow.png -------------------------------------------------------------------------------- /docs/performance/graph/images/encap_single_flow.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takehaya/Vinbero/e35cfdc1b06b083d5a7aae53e1ba99e65027ec15/docs/performance/graph/images/encap_single_flow.pdf -------------------------------------------------------------------------------- /docs/performance/graph/images/encap_single_flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takehaya/Vinbero/e35cfdc1b06b083d5a7aae53e1ba99e65027ec15/docs/performance/graph/images/encap_single_flow.png -------------------------------------------------------------------------------- /docs/performance/graph/images/end_multi_flow.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takehaya/Vinbero/e35cfdc1b06b083d5a7aae53e1ba99e65027ec15/docs/performance/graph/images/end_multi_flow.pdf -------------------------------------------------------------------------------- /docs/performance/graph/images/end_multi_flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takehaya/Vinbero/e35cfdc1b06b083d5a7aae53e1ba99e65027ec15/docs/performance/graph/images/end_multi_flow.png -------------------------------------------------------------------------------- /docs/performance/graph/images/end_single_flow.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takehaya/Vinbero/e35cfdc1b06b083d5a7aae53e1ba99e65027ec15/docs/performance/graph/images/end_single_flow.pdf -------------------------------------------------------------------------------- /docs/performance/graph/images/end_single_flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takehaya/Vinbero/e35cfdc1b06b083d5a7aae53e1ba99e65027ec15/docs/performance/graph/images/end_single_flow.png -------------------------------------------------------------------------------- /docs/performance/graph/images/mobile_v4_multi_flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takehaya/Vinbero/e35cfdc1b06b083d5a7aae53e1ba99e65027ec15/docs/performance/graph/images/mobile_v4_multi_flow.png -------------------------------------------------------------------------------- /docs/performance/graph/images/mobile_v4_single_flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takehaya/Vinbero/e35cfdc1b06b083d5a7aae53e1ba99e65027ec15/docs/performance/graph/images/mobile_v4_single_flow.png -------------------------------------------------------------------------------- /docs/performance/graph/mobile_v4_graph.py: -------------------------------------------------------------------------------- 1 | import seaborn as sns 2 | import matplotlib.pyplot as plt 3 | import numpy as np 4 | sns.set() 5 | 6 | hmgtp4d_single = [5.4525952, 3.69098752, 3.19291392, 2.95174144, 2.8272230400000002, 2.8181952719101124] 7 | hmgtp4d_multi = [67.80092416, 34.07872, 16.908288, 8.50132992, 4.25066496, 3.238568212134831] 8 | endmgtp4e_single = [5.24288, 3.1981568, 3.44981504, 3.67263744, 3.78667008, 3.624067164044944] 9 | endmgtp4e_multi = [4.94927872, 3.00941312, 3.3554432000000003, 3.5651584, 3.70016256, 3.631607485842697] 10 | 11 | left = np.arange(len(hmgtp4d_single)) 12 | labels = [64, 128, 256, 512, 1024, 1424] 13 | width = 0.3 14 | 15 | plt.title("Payload pps (single flow)") 16 | plt.ylabel("Mpps") 17 | plt.xlabel("byte") 18 | 19 | plt.bar(left, hmgtp4d_single, color='r', width=width, align='center', label="H.M.GTP4.D") 20 | plt.bar(left+width, endmgtp4e_single, color='b', width=width, align='center', label="End.M.GTP4.E") 21 | 22 | plt.xticks(left + width/2, labels) 23 | plt.legend(bbox_to_anchor=(1, 1), loc='upper right', borderaxespad=0, fontsize=18) 24 | 25 | plt.show() 26 | 27 | 28 | left = np.arange(len(hmgtp4d_multi)) 29 | plt.title("Payload pps (multi flow)") 30 | plt.ylabel("Mpps") 31 | plt.xlabel("byte") 32 | 33 | plt.bar(left, hmgtp4d_multi, color='r', width=width, align='center', label="H.M.GTP4.D") 34 | plt.bar(left+width, endmgtp4e_multi, color='b', width=width, align='center', label="End.M.GTP4.E") 35 | 36 | plt.xticks(left + width/2, labels) 37 | plt.legend(bbox_to_anchor=(1, 1), loc='upper right', borderaxespad=0, fontsize=18) 38 | plt.show() 39 | -------------------------------------------------------------------------------- /docs/performance/performance.md: -------------------------------------------------------------------------------- 1 | # Performance 2 | 3 | Using Trex to measure communication speed 4 | ![](../images/measurement_topology.png) 5 | 6 | **The result is about 3-5Mpps in single flow, which is 2-3 times better performance than linux!** 7 | 8 | ## Install 9 | in detel to check link 10 | cf. [https://trex-tgn.cisco.com/trex/doc/trex_manual.html](https://trex-tgn.cisco.com/trex/doc/trex_manual.html) 11 | 12 | This time I am using fedora33 and mellanox X5 EN 50GbE. 13 | However, fedora33 is trapped by the validation of the mellanox driver installation, so we need to do a work-around hack! 14 | 15 | ``` 16 | yum -y install perl-File-Tail perl-File-Copy perl-File-Compare perl-sigtrap 17 | yum -y install kernel-rpm-macros python-srpm-macros rpm-build python3-devel tk gcc-gfortran tcsh elfutils-libelf-devel 18 | wget http://www.mellanox.com/downloads/ofed/MLNX_OFED-5.2-1.0.4.0/MLNX_OFED_LINUX-5.2-1.0.4.0-fc32-x86_64.tgz 19 | tar xfvz MLNX_OFED_LINUX-5.2-1.0.4.0-fc32-x86_64.tgz 20 | cd MLNX_OFED_LINUX-5.2-1.0.4.0-fc32-x86_64/ 21 | ``` 22 | 23 | `mlnxofedinstall` check and rewrite 24 | 25 | ``` 26 | } elsif ($dist_rpm =~ /fedora-release(|-common)-(\d+)/ and 27 | ($2 >= 12) and ($2 <= 33)) { 28 | ``` 29 | 30 | lets install 31 | ``` 32 | ./mlnxofedinstall --add-kernel-support --distro fc32 33 | # reboot after run 34 | /etc/init.d/openibd restart 35 | ``` 36 | 37 | next, trex install 38 | ``` 39 | sudo su - 40 | mkdir -p /opt/trex 41 | cd /opt/trex 42 | wget --no-check-certificate --no-cache https://trex-tgn.cisco.com/trex/release/v2.87.tar.gz 43 | cd /opt/trex 44 | tar xzvf v2.87.tar.gz 45 | cd /opt/trex/v2.87 46 | ``` 47 | 48 | ## Usage 49 | ``` 50 | ./t-rex-64 -i 51 | 52 | # Open another console 53 | ./trex-console 54 | start -f ./workspace/srv6_end_m_gtp4_e_for_range_clients.py -t packet_len=64 --port 0 -m 100% 55 | ``` 56 | 57 | ## RouterSetup 58 | use irqaffinity. this mellanox nic case. 59 | 60 | ``` 61 | git clone https://github.com/Mellanox/mlnx-tools.git 62 | cd mlnx-tools/ 63 | ./set_irq_affinity.sh ens4f0np0 ens4f1np1 64 | ``` 65 | 66 | ## linux SRv6 conifg 67 | ``` 68 | ip sr tunsrc set fc00:2::2 69 | systemctl stop firewalld 70 | systemctl disable firewalld 71 | 72 | ip neigh add 10.1.0.1 lladdr 0c:42:a1:8e:8d:26 dev ens4f0 73 | ip neigh add 10.2.0.1 lladdr 0c:42:a1:8e:8d:27 dev ens4f1 74 | ip neigh add fc00:12::1 lladdr 0c:42:a1:8e:8d:26 dev ens4f0 75 | ip neigh add fc00:21::1 lladdr 0c:42:a1:8e:8d:27 dev ens4f1 76 | 77 | sudo sysctl -w net.ipv6.conf.ens4f0.seg6_enabled=1 78 | 79 | # end 80 | sudo ip -6 route add fc00:2::2/128 encap seg6local action End dev ens4f0 81 | 82 | # encap 83 | sudo ip route add 10.2.0.1/32 encap seg6 mode encap segs fc00:3::3 dev ens4f0 84 | 85 | # dx4 86 | sudo ip -6 route del fc00:2::2 87 | sudo ip -6 route add fc00:2::2/128 encap seg6local action End.DX4 nh4 10.2.0.2 dev ens4f0 88 | ``` 89 | 90 | ## Results 91 | ### Mobile v4 92 | ![](./graph/images/mobile_v4_single_flow.png) 93 | ![](./graph/images/mobile_v4_multi_flow.png) 94 | 95 | ### End 96 | ![](./graph/images/end_single_flow.png) 97 | ![](./graph/images/end_multi_flow.png) 98 | 99 | ### End.DX4 100 | ![](./graph/images/dx4_single_flow.png) 101 | ![](./graph/images/dx4_multi_flow.png) 102 | 103 | ### T.Encap 104 | ![](./graph/images/encap_single_flow.png) 105 | ![](./graph/images/encap_multi_flow.png) 106 | 107 | 108 | ## Memo 109 | Probably because RSS is not working for SRv6 in my measurement environment, and only encap is showing performance. 110 | I want to investigate how to enable RSS for SRv6.... 111 | 112 | ## Reference 113 | * https://github.com/tohojo/xdp-paper/blob/master/benchmarks/bench01_baseline.org 114 | 115 | -------------------------------------------------------------------------------- /docs/performance/router.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | systemctl stop irqbalance 4 | systemctl disable irqbalance 5 | 6 | # addr configuraiton 7 | ip link set lo up 8 | ip addr add fc00:2::2/128 dev lo 9 | 10 | ip link set ens4f0 up 11 | ip addr add fc00:12::2/64 dev ens4f0 12 | ip addr add 10.1.0.2/24 dev ens4f0 13 | 14 | ip link set ens4f1 up 15 | ip addr add fc00:21::2/64 dev ens4f1 16 | ip addr add 10.2.0.2/24 dev ens4f1 17 | 18 | ip -6 route add fc00:1::/48 via fc00:12::1 19 | ip -6 route add fc00:3::/48 via fc00:21::1 20 | 21 | sysctl net.ipv4.conf.all.rp_filter=0 22 | sysctl net.ipv4.conf.all.forwarding=1 23 | sysctl net.ipv6.conf.all.forwarding=1 24 | 25 | # seg6 26 | sysctl net.ipv6.conf.all.seg6_enabled=1 27 | sysctl net.ipv6.route.max_size=2147483647 28 | # offload disible 29 | disblelist=( rx tx tso ufo gso gro lro tx nocache copy sg txvlan rxvlan receive-hashing ntuple-filters rx-vlan-filter tx-gre-segmentation tx-gre-csum-segmentation tx-ipxip4-segmentation tx-ipxip6-segmentation tx-udp_tnl-segmentation tx-udp_tnl-csum-segmentation tx-gso-partial tx-vlan-stag-hw-insert rx-udp_tunnel-port-offload ) 30 | for i in ${disblelist[@]}; do 31 | /sbin/ethtool -K ens4f0 $i off 2>&1 > /dev/null; 32 | done 33 | ethtool -L ens4f0 combined $(nproc --all) 34 | ethtool -K ens4f0 rxhash on 35 | ethtool -K ens4f0 ntuple on 36 | # card balancing 37 | for proto in tcp4 udp4 tcp6 udp6; do 38 | /sbin/ethtool -N ens4f0 rx-flow-hash $proto sd 39 | done 40 | 41 | for i in ${disblelist[@]}; do 42 | /sbin/ethtool -K ens4f1 $i off 2>&1 > /dev/null; 43 | done 44 | 45 | ethtool -L ens4f1 combined $(nproc --all) 46 | ethtool -K ens4f1 rxhash on 47 | ethtool -K ens4f1 ntuple on 48 | 49 | for proto in tcp4 udp4 tcp6 udp6; do 50 | /sbin/ethtool -N ens4f1 rx-flow-hash $proto sd 51 | done 52 | cd mlnx-tools/ofed_scripts 53 | ./set_irq_affinity.sh ens4f0 ens4f1 54 | sudo ethtool -G ens4f0 rx 4080 tx 4080 55 | sudo ethtool -G ens4f1 rx 4080 tx 4080 56 | sudo ethtool --set-priv-flags ens4f0 rx_cqe_compress on 57 | sudo ethtool --set-priv-flags ens4f1 rx_cqe_compress on 58 | -------------------------------------------------------------------------------- /docs/performance/srv6_end_dx4_for_bench.py: -------------------------------------------------------------------------------- 1 | from trex_stl_lib.api import * 2 | 3 | 4 | class STLS1(object): 5 | def create_stream (self, packet_len, stream_count): 6 | addresses=["fc00:2::2"] 7 | base_pkt = \ 8 | Ether()/\ 9 | IPv6(src="fc00:1::1", dst=addresses[-1])/\ 10 | IPv6ExtHdrSegmentRouting(len=(16 * len(addresses)) // 8, segleft=len(addresses)-1, lastentry=len(addresses)-1, addresses=addresses)/ \ 11 | IP(src="10.1.0.1", dst="10.2.0.1")/\ 12 | UDP(sport=10053,dport=10053) 13 | 14 | base_pkt_len = len(base_pkt) 15 | base_pkt /= 'x' * max(0, packet_len - base_pkt_len) 16 | packets = [] 17 | for i in range(stream_count): 18 | packets.append(STLStream( 19 | packet = STLPktBuilder(pkt = base_pkt), 20 | mode = STLTXCont() 21 | )) 22 | return packets 23 | 24 | def get_streams (self, direction = 0, packet_len = 64, stream_count = 1, **kwargs): 25 | # create 1 stream 26 | return self.create_stream(packet_len - 4, stream_count) 27 | 28 | 29 | # dynamic load - used for trex console or simulator 30 | def register(): 31 | return STLS1() 32 | -------------------------------------------------------------------------------- /docs/performance/srv6_end_dx4_for_range_clients.py: -------------------------------------------------------------------------------- 1 | from trex_stl_lib.api import * 2 | 3 | class STLS1(object): 4 | 5 | def __init__ (self): 6 | self.num_clients =30000; # max is 16bit 7 | 8 | def create_stream (self, packet_len): 9 | addresses=["fc00:2::2"] 10 | base_pkt = \ 11 | Ether()/\ 12 | IPv6(src="fc00:1::1", dst=addresses[-1])/\ 13 | IPv6ExtHdrSegmentRouting(len=(16 * len(addresses)) // 8, segleft=len(addresses)-1, lastentry=len(addresses)-1, addresses=addresses)/ \ 14 | IP(src="10.1.0.1", dst="10.2.0.1")/\ 15 | UDP(sport=10053,dport=10053) 16 | 17 | pad = max(0, packet_len - len(base_pkt)) * 'x' 18 | 19 | vm = STLScVmRaw([ 20 | STLVmFlowVar( 21 | name="ipv6_src", 22 | min_value="16.0.0.0", 23 | max_value="18.0.0.254", 24 | size=4, 25 | op="random", 26 | ), 27 | STLVmWrFlowVar( 28 | fv_name="ipv6_src", 29 | pkt_offset= "IPv6.src", 30 | offset_fixup=12, 31 | ), 32 | ], 33 | ) 34 | 35 | return STLStream(packet = STLPktBuilder(pkt = base_pkt/pad,vm = vm), 36 | mode = STLTXCont()) 37 | 38 | def get_streams (self, direction = 0, packet_len = 64, **kwargs): 39 | # create 1 stream 40 | return [ self.create_stream(packet_len - 4)] 41 | 42 | 43 | # dynamic load - used for trex console or simulator 44 | def register(): 45 | return STLS1() 46 | -------------------------------------------------------------------------------- /docs/performance/srv6_end_for_bench.py: -------------------------------------------------------------------------------- 1 | from trex_stl_lib.api import * 2 | 3 | 4 | class STLS1(object): 5 | def create_stream (self, packet_len, stream_count): 6 | addresses=["fc00:3::3", "fc00:2::2"] 7 | base_pkt = \ 8 | Ether()/\ 9 | IPv6(src="fc00:1::1", dst=addresses[-1])/\ 10 | IPv6ExtHdrSegmentRouting(len=(16 * len(addresses)) // 8, segleft=len(addresses)-1, lastentry=len(addresses)-1, addresses=addresses)/ \ 11 | IP(src="192.168.1.1", dst="192.168.1.2")/\ 12 | UDP(sport=10053,dport=10053) 13 | 14 | base_pkt_len = len(base_pkt) 15 | base_pkt /= 'x' * max(0, packet_len - base_pkt_len) 16 | packets = [] 17 | for i in range(stream_count): 18 | packets.append(STLStream( 19 | packet = STLPktBuilder(pkt = base_pkt), 20 | mode = STLTXCont() 21 | )) 22 | return packets 23 | 24 | def get_streams (self, direction = 0, packet_len = 64, stream_count = 1, **kwargs): 25 | # create 1 stream 26 | return self.create_stream(packet_len - 4, stream_count) 27 | 28 | 29 | # dynamic load - used for trex console or simulator 30 | def register(): 31 | return STLS1() 32 | -------------------------------------------------------------------------------- /docs/performance/srv6_end_for_range_clients.py: -------------------------------------------------------------------------------- 1 | from trex_stl_lib.api import * 2 | 3 | class STLS1(object): 4 | 5 | def __init__ (self): 6 | self.num_clients =30000; # max is 16bit 7 | 8 | def create_stream (self, packet_len): 9 | addresses=["fc00:3::3", "fc00:2::2"] 10 | base_pkt = \ 11 | Ether()/\ 12 | IPv6(src="fc00:1::1", dst="fc00:2::2")/\ 13 | IPv6ExtHdrSegmentRouting(len=(16 * len(addresses)) // 8, segleft=len(addresses)-1, lastentry=len(addresses)-1, addresses=addresses)/ \ 14 | IP(src="192.168.1.1", dst="192.168.1.2")/\ 15 | UDP(sport=10053,dport=10053) 16 | 17 | pad = max(0, packet_len - len(base_pkt)) * 'x' 18 | 19 | vm = STLScVmRaw([ 20 | STLVmFlowVar( 21 | name="ipv6_src", 22 | min_value="16.0.0.0", 23 | max_value="18.0.0.254", 24 | size=4, 25 | op="random", 26 | ), 27 | STLVmWrFlowVar( 28 | fv_name="ipv6_src", 29 | pkt_offset= "IPv6.src", 30 | offset_fixup=12, 31 | ), 32 | ], 33 | ) 34 | 35 | return STLStream(packet = STLPktBuilder(pkt = base_pkt/pad,vm = vm), 36 | mode = STLTXCont()) 37 | 38 | def get_streams (self, direction = 0, packet_len = 64, **kwargs): 39 | # create 1 stream 40 | return [ self.create_stream(packet_len - 4)] 41 | 42 | 43 | # dynamic load - used for trex console or simulator 44 | def register(): 45 | return STLS1() 46 | -------------------------------------------------------------------------------- /docs/performance/srv6_end_m_gtp4_e_for_bench.py: -------------------------------------------------------------------------------- 1 | from trex_stl_lib.api import * 2 | 3 | import ipaddress as ipa 4 | 5 | def emb_v6_in_v4(v6str, v4str, prefix): 6 | v6 = ipa.ip_address(v6str).packed 7 | v4 = ipa.ip_address(v4str).packed 8 | off = prefix // 8 9 | shf = prefix % 8 10 | return emb_byte_in_byte(bytearray(v6), v4, len(v4), off, shf) 11 | 12 | def emb_byte_in_byte(dbyte, sbyte, size, offset, shift): 13 | for i in range(size): 14 | if shift !=0 : 15 | dbyte[offset+i] |= (sbyte[i] >> shift)&0xff 16 | dbyte[offset+1+i] |= (sbyte[i] << (8 - shift))&0xff 17 | else: 18 | dbyte[offset+i] = sbyte[i] 19 | return dbyte 20 | 21 | class STLS1(object): 22 | def create_stream (self, packet_len, stream_count): 23 | saddr = ipa.ip_address(bytes(emb_v6_in_v4("fc00:1::", "10.1.0.1", 64))) 24 | daddr = ipa.ip_address(bytes(emb_v6_in_v4("fc00:2::", "10.2.0.1", 48))) 25 | 26 | addresses=[str(daddr)] 27 | base_pkt = \ 28 | Ether()/\ 29 | IPv6(src=str(saddr), dst=addresses[-1])/\ 30 | IPv6ExtHdrSegmentRouting(len=(16 * len(addresses)) // 8, segleft=len(addresses)-1, lastentry=len(addresses)-1, addresses=addresses)/ \ 31 | IP(src="192.168.1.1", dst="192.168.1.2")/\ 32 | UDP(dport=80) 33 | 34 | base_pkt_len = len(base_pkt) 35 | base_pkt /= 'x' * max(0, packet_len - base_pkt_len) 36 | packets = [] 37 | for i in range(stream_count): 38 | packets.append(STLStream( 39 | packet = STLPktBuilder(pkt = base_pkt), 40 | mode = STLTXCont() 41 | )) 42 | return packets 43 | 44 | def get_streams (self, direction = 0, packet_len = 64, stream_count = 1, **kwargs): 45 | # create 1 stream 46 | return self.create_stream(packet_len - 4, stream_count) 47 | 48 | 49 | # dynamic load - used for trex console or simulator 50 | def register(): 51 | return STLS1() 52 | -------------------------------------------------------------------------------- /docs/performance/srv6_end_m_gtp4_e_for_range_clients.py: -------------------------------------------------------------------------------- 1 | from trex_stl_lib.api import * 2 | 3 | import ipaddress as ipa 4 | 5 | def emb_v6_in_v4(v6str, v4str, prefix): 6 | v6 = ipa.ip_address(v6str).packed 7 | v4 = ipa.ip_address(v4str).packed 8 | off = prefix // 8 9 | shf = prefix % 8 10 | return emb_byte_in_byte(bytearray(v6), v4, len(v4), off, shf) 11 | 12 | def emb_byte_in_byte(dbyte, sbyte, size, offset, shift): 13 | for i in range(size): 14 | if shift !=0 : 15 | dbyte[offset+i] |= (sbyte[i] >> shift)&0xff 16 | dbyte[offset+1+i] |= (sbyte[i] << (8 - shift))&0xff 17 | else: 18 | dbyte[offset+i] = sbyte[i] 19 | return dbyte 20 | 21 | 22 | class STLS1(object): 23 | 24 | def __init__ (self): 25 | self.num_clients =30000; # max is 16bit 26 | 27 | def create_stream (self, packet_len): 28 | saddr = ipa.ip_address(bytes(emb_v6_in_v4("fc00:1::", "10.1.0.1", 64))) 29 | daddr = ipa.ip_address(bytes(emb_v6_in_v4("fc00:2::", "10.2.0.1", 48))) 30 | 31 | addresses=[str(daddr)] 32 | base_pkt = \ 33 | Ether()/\ 34 | IPv6(src=str(saddr), dst=addresses[-1])/\ 35 | IPv6ExtHdrSegmentRouting(len=(16 * len(addresses)) // 8, segleft=len(addresses)-1, lastentry=len(addresses)-1, addresses=addresses)/ \ 36 | IP(src="192.168.1.1", dst="192.168.1.2")/\ 37 | UDP(dport=80) 38 | 39 | pad = max(0, packet_len - len(base_pkt)) * 'x' 40 | 41 | vm = STLScVmRaw([ 42 | STLVmFlowVar( 43 | name="ipv6_src", 44 | min_value="16.0.0.0", 45 | max_value="18.0.0.254", 46 | size=4, 47 | op="random", 48 | ), 49 | STLVmWrFlowVar( 50 | fv_name="ipv6_src", 51 | pkt_offset= "IPv6.src", 52 | offset_fixup=12, 53 | ), 54 | ], 55 | ) 56 | 57 | return STLStream(packet = STLPktBuilder(pkt = base_pkt/pad,vm = vm), 58 | mode = STLTXCont()) 59 | 60 | def get_streams (self, direction = 0, packet_len = 64, **kwargs): 61 | # create 1 stream 62 | return [ self.create_stream(packet_len - 4)] 63 | 64 | 65 | # dynamic load - used for trex console or simulator 66 | def register(): 67 | return STLS1() 68 | -------------------------------------------------------------------------------- /docs/performance/srv6_hmgtp4d_for_bench.py: -------------------------------------------------------------------------------- 1 | from trex_stl_lib.api import * 2 | from scapy.contrib.gtp import GTPHeader as GTPHeader 3 | 4 | class STLS1(object): 5 | def create_stream (self, packet_len, stream_count): 6 | base_pkt = \ 7 | Ether()/\ 8 | IP(src="10.1.0.1", dst="10.2.0.1")/\ 9 | UDP(dport=2152,sport=2152)/\ 10 | GTPHeader(version=1, teid=1111,gtp_type=0xff)/\ 11 | IP(src="10.0.1.1", dst="10.0.1.2")/\ 12 | UDP(sport=10053,dport=10053) 13 | 14 | 15 | base_pkt_len = len(base_pkt) 16 | base_pkt /= 'x' * max(0, packet_len - base_pkt_len) 17 | packets = [] 18 | for i in range(stream_count): 19 | packets.append(STLStream( 20 | packet = STLPktBuilder(pkt = base_pkt), 21 | mode = STLTXCont() 22 | )) 23 | return packets 24 | 25 | def get_streams (self, direction = 0, packet_len = 64, stream_count = 1, **kwargs): 26 | # create 1 stream 27 | return self.create_stream(packet_len - 4, stream_count) 28 | 29 | 30 | # dynamic load - used for trex console or simulator 31 | def register(): 32 | return STLS1() 33 | 34 | -------------------------------------------------------------------------------- /docs/performance/srv6_hmgtp4d_for_range_clients.py: -------------------------------------------------------------------------------- 1 | from trex_stl_lib.api import * 2 | from scapy.contrib.gtp import GTPHeader as GTPHeader 3 | 4 | class STLS1(object): 5 | 6 | def __init__ (self): 7 | self.num_clients =30000; # max is 16bit 8 | self.fsize =64 9 | 10 | def create_stream (self): 11 | # Create base packet and pad it to size 12 | size = self.fsize - 4; # HW will add 4 bytes ethernet FCS 13 | base_pkt = \ 14 | Ether()/\ 15 | IP(src="10.1.0.1", dst="10.2.0.1")/\ 16 | UDP(dport=2152,sport=2152)/\ 17 | GTPHeader(version=1, teid=1111,gtp_type=0xff)/\ 18 | IP(src="10.0.1.1", dst="10.0.1.2")/\ 19 | UDP(sport=10053,dport=10053) 20 | pad = max(0, size - len(base_pkt)) * 'x' 21 | 22 | vm = STLScVmRaw([ 23 | STLVmFlowVar( 24 | name="ip_src", 25 | min_value="10.1.0.1", 26 | max_value="10.1.0.255", 27 | size=4, 28 | step=1, 29 | op="inc" 30 | ), 31 | STLVmWrFlowVar( 32 | fv_name="ip_src", 33 | pkt_offset= "IP.src", 34 | ), # write ip to packet IP.src 35 | STLVmFixIpv4(offset = "IP") # fix checksum 36 | ], 37 | cache_size = 255 # cache the packets, much better performance 38 | ) 39 | 40 | return STLStream(packet = STLPktBuilder(pkt = base_pkt/pad,vm = vm), 41 | mode = STLTXCont( pps=10 )) 42 | 43 | def get_streams (self, direction = 0, **kwargs): 44 | # create 1 stream 45 | return [ self.create_stream() ] 46 | 47 | 48 | # dynamic load - used for trex console or simulator 49 | def register(): 50 | return STLS1() 51 | -------------------------------------------------------------------------------- /docs/performance/srv6_t_encap_for_bench.py: -------------------------------------------------------------------------------- 1 | from trex_stl_lib.api import * 2 | 3 | 4 | class STLS1(object): 5 | def create_stream (self, packet_len, stream_count): 6 | base_pkt = \ 7 | Ether()/\ 8 | IP(src="10.1.0.1", dst="10.2.0.1")/\ 9 | UDP(sport=10053,dport=10053) 10 | 11 | base_pkt_len = len(base_pkt) 12 | base_pkt /= 'x' * max(0, packet_len - base_pkt_len) 13 | packets = [] 14 | for i in range(stream_count): 15 | packets.append(STLStream( 16 | packet = STLPktBuilder(pkt = base_pkt), 17 | mode = STLTXCont() 18 | )) 19 | return packets 20 | 21 | def get_streams (self, direction = 0, packet_len = 64, stream_count = 1, **kwargs): 22 | # create 1 stream 23 | return self.create_stream(packet_len - 4, stream_count) 24 | 25 | 26 | # dynamic load - used for trex console or simulator 27 | def register(): 28 | return STLS1() 29 | -------------------------------------------------------------------------------- /docs/performance/srv6_t_encap_for_range_clients.py: -------------------------------------------------------------------------------- 1 | from trex_stl_lib.api import * 2 | class STLS1(object): 3 | 4 | def __init__ (self): 5 | self.num_clients =30000; # max is 16bit 6 | 7 | def create_stream (self, packet_len): 8 | base_pkt = \ 9 | Ether()/\ 10 | IP(src="10.1.0.1", dst="10.2.0.1")/\ 11 | UDP(dport=10053,sport=10053) 12 | 13 | pad = max(0, packet_len - len(base_pkt)) * 'x' 14 | 15 | vm = STLScVmRaw([ 16 | STLVmFlowVar( 17 | name="ip_src", 18 | min_value="10.1.0.1", 19 | max_value="10.1.0.255", 20 | size=4, 21 | step=1, 22 | op="inc" 23 | ), 24 | STLVmWrFlowVar( 25 | fv_name="ip_src", 26 | pkt_offset= "IP.src", 27 | ), # write ip to packet IP.src 28 | STLVmFixIpv4(offset = "IP") # fix checksum 29 | ], 30 | cache_size = 255 # cache the packets, much better performance 31 | ) 32 | 33 | return STLStream(packet = STLPktBuilder(pkt = base_pkt/pad,vm = vm), 34 | mode = STLTXCont()) 35 | 36 | def get_streams (self, direction = 0, packet_len = 64, **kwargs): 37 | # create 1 stream 38 | return [ self.create_stream(packet_len - 4)] 39 | 40 | 41 | # dynamic load - used for trex console or simulator 42 | def register(): 43 | return STLS1() 44 | -------------------------------------------------------------------------------- /docs/performance/trafficgen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # addr configuraiton 3 | ip link set lo up 4 | ip addr add fc00:1::1/128 dev lo 5 | ip addr add fc00:3::3/128 dev lo 6 | 7 | ip link set ens2f0 up 8 | ip addr add fc00:12::1/64 dev ens2f0 9 | ip addr add 10.1.0.1/24 dev ens2f0 10 | 11 | ip link set ens2f1 up 12 | ip addr add fc00:21::1/64 dev ens2f1 13 | ip addr add 10.2.0.1/24 dev ens2f1 14 | 15 | # ip -6 route add fc00:21::/48 via fc00:21::2 16 | # ip -6 route add fc00:12::/48 via fc00:12::2 17 | ip -6 route add fc00:2::/48 via fc00:12::2 18 | 19 | sysctl net.ipv4.conf.all.rp_filter=0 20 | sysctl net.ipv4.conf.all.forwarding=1 21 | sysctl net.ipv6.conf.all.forwarding=1 22 | -------------------------------------------------------------------------------- /docs/performance/trex_cfg.yaml: -------------------------------------------------------------------------------- 1 | - version: 2 2 | interfaces: ['01:00.0', '01:00.1'] 3 | # port_info: 4 | # - ip: 10.1.0.1 5 | # default_gw: 10.2.0.1 6 | # - ip: 10.2.0.1 7 | # default_gw: 10.1.0.1 8 | port_info: # set eh mac addr 9 | - dest_mac: [0x0c,0x42,0xa1,0x8e,0x8d,0xae] # router mac addr should be taken from DUT 10 | src_mac: [0x0c,0x42,0xa1,0x8e,0x8d,0x26] # source mac-addr - taken from ifconfig 11 | - dest_mac: [0x0c,0x42,0xa1,0x8e,0x8d,0xaf] # router mac addr taken from DUT 12 | src_mac: [0x0c,0x42,0xa1,0x8e,0x8d,0x27] #source mac-addr taken from ifconfig 13 | platform: 14 | master_thread_id: 0 15 | latency_thread_id: 15 16 | dual_if: 17 | - socket: 0 18 | threads: [1,2,3,4,5,6,7,8,9,10,11,12,13,14] 19 | 20 | -------------------------------------------------------------------------------- /docs/tips.md: -------------------------------------------------------------------------------- 1 | # Tips 2 | ## Print debug 3 | ``` 4 | sudo trace-cmd record -e 'xdp:*' -O trace_printk 5 | sudo trace-cmd report > trace.log 6 | ``` 7 | ## Xdpcap 8 | Packets output at runtime can be saved in pcap. 9 | See https://github.com/cloudflare/xdpcap 10 | 11 | ``` 12 | sudo apt-get install libpcap-dev 13 | # centos/fedora case 14 | # sudo yum install libpcap-devel 15 | go get -u github.com/cloudflare/xdpcap/cmd/xdpcap 16 | 17 | # run on each nodes 18 | sudo mount bpffs /sys/fs/bpf -t bpf 19 | 20 | # capture packets 21 | xdpcap /sys/fs/bpf/xdpcap_hook "icmp" 22 | ``` 23 | -------------------------------------------------------------------------------- /gen_bpf_helper.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | LINUX_VERSION=5.4.49 4 | 5 | wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-${LINUX_VERSION}.tar.xz 6 | tar xf linux-${LINUX_VERSION}.tar.xz 7 | pushd linux-${LINUX_VERSION}/ 8 | 9 | make defconfig 10 | ./scripts/bpf_helpers_doc.py --filename ./tools/include/uapi/linux/bpf.h > ../include/bpf_helper_defs.h 11 | cp ./tools/testing/selftests/bpf/bpf_helpers.h ../include/bpf_helpers.h 12 | 13 | popd 14 | rm linux-${LINUX_VERSION}.tar.xz 15 | rm -rf linux-${LINUX_VERSION}/ 16 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/takehaya/vinbero 2 | 3 | go 1.14 4 | 5 | require ( 6 | github.com/cilium/ebpf v0.3.0 7 | github.com/kr/pretty v0.2.1 8 | github.com/pkg/errors v0.9.1 9 | github.com/urfave/cli v1.22.5 10 | github.com/vishvananda/netlink v1.1.0 11 | github.com/wmnsk/go-gtp v0.7.15 12 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f 13 | gopkg.in/yaml.v2 v2.2.7 14 | ) 15 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 3 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 4 | github.com/cilium/ebpf v0.3.0 h1:LI3lsl5GmTh+OFYamrj8sp+R0yam38zHG6NTDhSlNmQ= 5 | github.com/cilium/ebpf v0.3.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs= 6 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 7 | github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= 8 | github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY= 9 | github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= 10 | github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 11 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 12 | github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= 13 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 14 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 15 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 16 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 17 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 18 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 19 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 20 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 21 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 22 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 23 | github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= 24 | github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 25 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 26 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 27 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 28 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 29 | github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 30 | github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM= 31 | github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 32 | github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 33 | github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 34 | github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= 35 | github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= 36 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 37 | github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= 38 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 39 | github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= 40 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 41 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 42 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 43 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 44 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 45 | github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= 46 | github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 47 | github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= 48 | github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= 49 | github.com/urfave/cli v1.22.5 h1:lNq9sAHXK2qfdI8W+GRItjCEkI+2oR4d+MEHy1CKXoU= 50 | github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= 51 | github.com/vishvananda/netlink v1.1.0 h1:1iyaYNBLmP6L0220aDnYQpo1QEV4t4hJ+xEEhhJH8j0= 52 | github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= 53 | github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df h1:OviZH7qLw/7ZovXvuNyL3XQl8UFofeikI1NW1Gypu7k= 54 | github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= 55 | github.com/wmnsk/go-gtp v0.7.15 h1:D0L2ISdfsVwzHvN/l7uyTvdqewUl+p0BDFElN0p7Uy0= 56 | github.com/wmnsk/go-gtp v0.7.15/go.mod h1:v1psjZ7skpPSDegH23Amg9rNufs0BoXNM+GBtW5t58I= 57 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 58 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 59 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 60 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 61 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 62 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 63 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 64 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 65 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 66 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 67 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 68 | golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 69 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 70 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 71 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 72 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 73 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 74 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 75 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 76 | golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 77 | golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 78 | golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae h1:Ih9Yo4hSPImZOpfGuA4bR/ORKTAbhZo2AbWNRCnevdo= 79 | golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 80 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA= 81 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 82 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 83 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 84 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 85 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 86 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 87 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 88 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 89 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= 90 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 91 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 92 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 93 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 94 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 95 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= 96 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 97 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 98 | google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= 99 | google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 100 | google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= 101 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 102 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 103 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 104 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 105 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 106 | google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 107 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 108 | google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 109 | google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= 110 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 111 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 112 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 113 | gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo= 114 | gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 115 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 116 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 117 | -------------------------------------------------------------------------------- /include/bpf_endian.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0 */ 2 | #ifndef __BPF_ENDIAN__ 3 | #define __BPF_ENDIAN__ 4 | 5 | #include 6 | 7 | /* LLVM's BPF target selects the endianness of the CPU 8 | * it compiles on, or the user specifies (bpfel/bpfeb), 9 | * respectively. The used __BYTE_ORDER__ is defined by 10 | * the compiler, we cannot rely on __BYTE_ORDER from 11 | * libc headers, since it doesn't reflect the actual 12 | * requested byte order. 13 | * 14 | * Note, LLVM's BPF target has different __builtin_bswapX() 15 | * semantics. It does map to BPF_ALU | BPF_END | BPF_TO_BE 16 | * in bpfel and bpfeb case, which means below, that we map 17 | * to cpu_to_be16(). We could use it unconditionally in BPF 18 | * case, but better not rely on it, so that this header here 19 | * can be used from application and BPF program side, which 20 | * use different targets. 21 | */ 22 | #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ 23 | # define __bpf_ntohs(x) __builtin_bswap16(x) 24 | # define __bpf_htons(x) __builtin_bswap16(x) 25 | # define __bpf_constant_ntohs(x) ___constant_swab16(x) 26 | # define __bpf_constant_htons(x) ___constant_swab16(x) 27 | # define __bpf_ntohl(x) __builtin_bswap32(x) 28 | # define __bpf_htonl(x) __builtin_bswap32(x) 29 | # define __bpf_constant_ntohl(x) ___constant_swab32(x) 30 | # define __bpf_constant_htonl(x) ___constant_swab32(x) 31 | #elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ 32 | # define __bpf_ntohs(x) (x) 33 | # define __bpf_htons(x) (x) 34 | # define __bpf_constant_ntohs(x) (x) 35 | # define __bpf_constant_htons(x) (x) 36 | # define __bpf_ntohl(x) (x) 37 | # define __bpf_htonl(x) (x) 38 | # define __bpf_constant_ntohl(x) (x) 39 | # define __bpf_constant_htonl(x) (x) 40 | #else 41 | # error "Fix your compiler's __BYTE_ORDER__?!" 42 | #endif 43 | 44 | #define bpf_htons(x) \ 45 | (__builtin_constant_p(x) ? \ 46 | __bpf_constant_htons(x) : __bpf_htons(x)) 47 | #define bpf_ntohs(x) \ 48 | (__builtin_constant_p(x) ? \ 49 | __bpf_constant_ntohs(x) : __bpf_ntohs(x)) 50 | #define bpf_htonl(x) \ 51 | (__builtin_constant_p(x) ? \ 52 | __bpf_constant_htonl(x) : __bpf_htonl(x)) 53 | #define bpf_ntohl(x) \ 54 | (__builtin_constant_p(x) ? \ 55 | __bpf_constant_ntohl(x) : __bpf_ntohl(x)) 56 | 57 | #endif /* __BPF_ENDIAN__ */ 58 | -------------------------------------------------------------------------------- /internal/app.go: -------------------------------------------------------------------------------- 1 | package internal 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "os/signal" 7 | "syscall" 8 | 9 | "github.com/kr/pretty" 10 | "github.com/pkg/errors" 11 | "github.com/takehaya/vinbero/pkg/config" 12 | "github.com/takehaya/vinbero/pkg/coreelf" 13 | "github.com/takehaya/vinbero/pkg/srv6" 14 | "github.com/takehaya/vinbero/pkg/xdptool" 15 | ) 16 | 17 | func App(c *config.Config) error { 18 | obj, err := coreelf.ReadCollection() 19 | if err != nil { 20 | return errors.WithStack(err) 21 | } 22 | 23 | for _, dev := range c.InternalConfig.Devices { 24 | err = xdptool.Attach(obj.ProgramSrv6Handler, dev) 25 | if err != nil { 26 | return errors.WithStack(err) 27 | } 28 | fmt.Println("attached device: ", dev) 29 | } 30 | 31 | //xdpcap 32 | xdpcapHook := obj.MapXdpcapHook 33 | path := "/sys/fs/bpf/xdpcap_hook" 34 | os.RemoveAll(path) 35 | err = xdpcapHook.Pin(path) 36 | if err != nil { 37 | _ = disposeDevice(&c.InternalConfig) 38 | return errors.WithStack(err) 39 | } 40 | 41 | //addrheep 42 | os.RemoveAll(srv6.V6addrHeepPath) 43 | v6heep, err := setV6addrHeep(obj.MapInTapleV6Addr) 44 | if err != nil { 45 | return errors.WithStack(err) 46 | } 47 | fmt.Printf("%# v\n", pretty.Formatter(v6heep)) 48 | 49 | //txport 50 | txm, err := setTxportDevice(&c.InternalConfig, obj.MapTxPort) 51 | if err != nil { 52 | _ = disposeDevice(&c.InternalConfig) 53 | return errors.WithStack(err) 54 | } 55 | fmt.Printf("%# v\n", pretty.Formatter(txm)) 56 | 57 | //function map 58 | var srfn *srv6.FunctionTablesMap 59 | if funcs := c.Setting.Functions; 0 < len(funcs) { 60 | srfn, err = setSrv6Function(funcs, obj.MapFunctionTable) 61 | if err != nil { 62 | _ = disposeDevice(&c.InternalConfig) 63 | return errors.WithStack(err) 64 | } 65 | } 66 | fmt.Printf("%# v\n", pretty.Formatter(srfn)) 67 | 68 | // tran v4 69 | var tran4 *srv6.TransitTablev4sMap 70 | if t4c := c.Setting.Transitv4; 0 < len(t4c) { 71 | tran4, err = setTransitv4(t4c, obj.MapTransitTableV4) 72 | if err != nil { 73 | _ = disposeDevice(&c.InternalConfig) 74 | return errors.WithStack(err) 75 | } 76 | } 77 | fmt.Printf("%# v\n", pretty.Formatter(tran4)) 78 | 79 | // tran v6 80 | var tran6 *srv6.TransitTablev6sMap 81 | if t6c := c.Setting.Transitv6; 0 < len(t6c) { 82 | tran6, err = setTransitv6(t6c, obj.MapTransitTableV6) 83 | if err != nil { 84 | _ = disposeDevice(&c.InternalConfig) 85 | return errors.WithStack(err) 86 | } 87 | } 88 | fmt.Printf("%# v\n", pretty.Formatter(tran6)) 89 | 90 | signalChan := make(chan os.Signal, 1) 91 | signal.Notify(signalChan, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT) 92 | 93 | fmt.Println("XDP program successfully loaded and attached.") 94 | fmt.Println("Press CTRL+C to stop.") 95 | for { 96 | select { 97 | case <-signalChan: 98 | // TODO: change prepare priority elf->ebpfmap make -> xdp attach 99 | err := disposeDevice(&c.InternalConfig) 100 | if err != nil { 101 | return errors.WithStack(err) 102 | } 103 | return nil 104 | } 105 | } 106 | } 107 | 108 | func disposeDevice(c *config.InternalConfig) error { 109 | for _, dev := range c.Devices { 110 | // Attach to interface 111 | err := xdptool.Detach(dev) 112 | if err != nil { 113 | return errors.WithStack(err) 114 | } 115 | fmt.Println("attached device: ", dev) 116 | } 117 | return nil 118 | } 119 | -------------------------------------------------------------------------------- /internal/load.go: -------------------------------------------------------------------------------- 1 | package internal 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "strconv" 7 | 8 | "github.com/cilium/ebpf" 9 | "github.com/kr/pretty" 10 | "github.com/pkg/errors" 11 | "github.com/takehaya/vinbero/pkg/config" 12 | "github.com/takehaya/vinbero/pkg/srv6" 13 | ) 14 | 15 | func setV6addrHeep(m *ebpf.Map) (*srv6.V6addrHeepMap, error) { 16 | // txport map 17 | v6heep := srv6.MappingV6addrHeep(m) 18 | 19 | // write to redirect map 20 | h := []*srv6.V6addrHeep{} 21 | err := v6heep.Update(h, 0) 22 | if err != nil { 23 | fmt.Printf("Unable to Insert into eBPF map: %v", err) 24 | return nil, errors.WithStack(err) 25 | } 26 | return v6heep, nil 27 | } 28 | 29 | func setTxportDevice(c *config.InternalConfig, m *ebpf.Map) (*srv6.TxPortsMap, error) { 30 | // txport map 31 | txm := srv6.MappingTxPort(m) 32 | 33 | // write to redirect map 34 | for _, dev := range c.Devices { 35 | iface, err := net.InterfaceByName(dev) 36 | if err != nil { 37 | fmt.Printf("fail to interface: %v", dev) 38 | return nil, errors.WithStack(err) 39 | } 40 | fmt.Printf("%v : %v\n", dev, iface) 41 | err = txm.Update(srv6.TxPort{Iface: uint32(iface.Index)}, iface.Index) 42 | if err != nil { 43 | fmt.Printf("Unable to Insert into eBPF map: %v", err) 44 | return nil, errors.WithStack(err) 45 | } 46 | } 47 | return txm, nil 48 | } 49 | 50 | func setSrv6Function(c []config.FunctionsConfig, m *ebpf.Map) (*srv6.FunctionTablesMap, error) { 51 | fnm := srv6.MappingFunctionTable(m) 52 | 53 | for _, fn := range c { 54 | actId, err := srv6.Seg6LocalActionInt(fn.Action) 55 | if err != nil { 56 | return nil, errors.WithStack(err) 57 | } 58 | 59 | _, cidr, err := net.ParseCIDR(fn.TriggerAddr) 60 | if err != nil { 61 | return nil, errors.WithStack(err) 62 | } 63 | prefixlen, _ := cidr.Mask.Size() 64 | 65 | sip := net.ParseIP(fn.SAddr) 66 | var convip, startSip, nhpip [16]byte 67 | var flaverid uint32 68 | copy(convip[:], cidr.IP.To16()) 69 | if sip != nil { 70 | copy(startSip[:], sip.To16()) 71 | } 72 | 73 | nip := net.ParseIP(fn.Nexthop) 74 | switch actId { 75 | case srv6.SEG6_LOCAL_ACTION_END_DX4: 76 | copy(nhpip[:], nip.To4()) 77 | case srv6.SEG6_LOCAL_ACTION_END_DX6: 78 | copy(nhpip[:], nip.To16()) 79 | 80 | case srv6.SEG6_LOCAL_ACTION_END: 81 | case srv6.SEG6_LOCAL_ACTION_END_X: 82 | case srv6.SEG6_LOCAL_ACTION_END_T: 83 | flaverid, err = srv6.Seg6LocalFlaverInt(fn.Flaver) 84 | if err != nil { 85 | return nil, errors.WithStack(err) 86 | } 87 | } 88 | 89 | var v4Spos, v4Dpos uint32 90 | if actId == srv6.SEG6_LOCAL_ACTION_END_M_GTP4_E { 91 | pos, err := strconv.Atoi(fn.V4AddrSPos) 92 | v4Spos = uint32(pos) 93 | if err != nil { 94 | return nil, errors.WithStack(err) 95 | } 96 | 97 | pos, err = strconv.Atoi(fn.V4AddrDPos) 98 | v4Dpos = uint32(pos) 99 | if err != nil { 100 | return nil, errors.WithStack(err) 101 | } 102 | } 103 | 104 | err = fnm.Update( 105 | srv6.FunctionTable{ 106 | Function: actId, 107 | StartSaddr: startSip, 108 | Nexthop: nhpip, 109 | Flaver: flaverid, 110 | V4AddrSPos: v4Spos, 111 | V4AddrDPos: v4Dpos, 112 | }, 113 | convip, 114 | uint32(prefixlen), 115 | ) 116 | if err != nil { 117 | fmt.Printf("Unable to Insert into eBPF map: %v", err) 118 | return nil, errors.WithStack(err) 119 | } 120 | fmt.Printf("Insert into eBPF map seg6 action: %v", fn) 121 | } 122 | fmt.Printf("%# v\n", pretty.Formatter(fnm)) 123 | 124 | return fnm, nil 125 | } 126 | 127 | func setTransitv4(c []config.Transitv4Config, m *ebpf.Map) (*srv6.TransitTablev4sMap, error) { 128 | tranv4 := srv6.MappingTransitTablev4(m) 129 | 130 | for _, t4 := range c { 131 | actId, err := srv6.Seg6EncapModeInt(t4.Action) 132 | if err != nil { 133 | return nil, errors.WithStack(err) 134 | } 135 | 136 | _, cidr, err := net.ParseCIDR(t4.TriggerAddr) 137 | if err != nil { 138 | return nil, errors.WithStack(err) 139 | } 140 | prefixlen, _ := cidr.Mask.Size() 141 | 142 | sip := net.ParseIP(t4.SAddr) 143 | var convip [4]byte 144 | copy(convip[:], cidr.IP.To4()) 145 | 146 | var ( 147 | actSip, actDip [16]byte 148 | sPrefix, dPrefix uint32 149 | ) 150 | if actId == srv6.SEG6_IPTUN_MODE_ENCAP_H_M_GTP4_D { 151 | _, srcCidr, err := net.ParseCIDR(t4.SAddr) 152 | if err != nil { 153 | return nil, errors.WithStack(err) 154 | } 155 | copy(actSip[:], srcCidr.IP.To16()) 156 | srcPrefixlen, _ := srcCidr.Mask.Size() 157 | sPrefix = uint32(srcPrefixlen) 158 | 159 | _, dstCidr, err := net.ParseCIDR(t4.DAddr) 160 | if err != nil { 161 | return nil, errors.WithStack(err) 162 | } 163 | copy(actDip[:], dstCidr.IP.To16()) 164 | dstPrefixlen, _ := dstCidr.Mask.Size() 165 | dPrefix = uint32(dstPrefixlen) 166 | } else { 167 | if sip != nil { 168 | copy(actSip[:], sip.To16()) 169 | } 170 | } 171 | 172 | var newsegments [srv6.MAX_SEGMENTS][16]byte 173 | for i, seg := range t4.Segments { 174 | var segmentaddr [16]byte 175 | copy(segmentaddr[:], net.ParseIP(seg).To16()) 176 | fmt.Println("segments addr", i, segmentaddr) 177 | newsegments[i] = segmentaddr 178 | fmt.Println("seg: ", newsegments[i]) 179 | } 180 | fmt.Println("segment len is ", len(t4.Segments)) 181 | err = tranv4.Update( 182 | srv6.TransitTablev4{ 183 | Saddr: actSip, 184 | Daddr: actDip, 185 | SPrefixlen: sPrefix, 186 | DPrefixlen: dPrefix, 187 | SegmentLength: uint32(len(t4.Segments)), 188 | Action: uint32(actId), 189 | Segments: newsegments, 190 | }, 191 | convip, 192 | uint32(prefixlen), 193 | ) 194 | if err != nil { 195 | fmt.Printf("Unable to Insert into eBPF map: %v", err) 196 | return nil, errors.WithStack(err) 197 | } 198 | } 199 | fmt.Printf("%# v\n", pretty.Formatter(tranv4)) 200 | 201 | return tranv4, nil 202 | } 203 | 204 | func setTransitv6(c []config.Transitv6Config, m *ebpf.Map) (*srv6.TransitTablev6sMap, error) { 205 | tranv6 := srv6.MappingTransitTablev6(m) 206 | 207 | for _, t6 := range c { 208 | actId, err := srv6.Seg6EncapModeInt(t6.Action) 209 | if err != nil { 210 | return nil, errors.WithStack(err) 211 | } 212 | 213 | _, cidr, err := net.ParseCIDR(t6.TriggerAddr) 214 | if err != nil { 215 | return nil, errors.WithStack(err) 216 | } 217 | prefixlen, _ := cidr.Mask.Size() 218 | 219 | sip := net.ParseIP(t6.SAddr) 220 | var convip [16]byte 221 | copy(convip[:], cidr.IP.To16()) 222 | 223 | var ( 224 | actSip, actDip [16]byte 225 | sPrefix, dPrefix uint32 226 | ) 227 | 228 | if sip != nil { 229 | copy(actSip[:], sip.To16()) 230 | } 231 | 232 | var newsegments [srv6.MAX_SEGMENTS][16]byte 233 | for i, seg := range t6.Segments { 234 | var segmentaddr [16]byte 235 | copy(segmentaddr[:], net.ParseIP(seg).To16()) 236 | fmt.Println("segments addr", i, segmentaddr) 237 | newsegments[i] = segmentaddr 238 | fmt.Println("seg: ", newsegments[i]) 239 | } 240 | fmt.Println("segment len is ", len(t6.Segments)) 241 | err = tranv6.Update( 242 | srv6.TransitTablev6{ 243 | Saddr: actSip, 244 | Daddr: actDip, 245 | SPrefixlen: sPrefix, 246 | DPrefixlen: dPrefix, 247 | SegmentLength: uint32(len(t6.Segments)), 248 | Action: uint32(actId), 249 | Segments: newsegments, 250 | }, 251 | convip, 252 | uint32(prefixlen), 253 | ) 254 | if err != nil { 255 | fmt.Printf("Unable to Insert into eBPF map: %v", err) 256 | return nil, errors.WithStack(err) 257 | } 258 | } 259 | fmt.Printf("%# v\n", pretty.Formatter(tranv6)) 260 | 261 | return tranv6, nil 262 | } 263 | -------------------------------------------------------------------------------- /pkg/config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "io/ioutil" 5 | "os" 6 | "sync" 7 | 8 | "github.com/pkg/errors" 9 | "github.com/takehaya/vinbero/pkg/utils" 10 | "gopkg.in/yaml.v2" 11 | ) 12 | 13 | // LoadFile parses the given YAML file into a Config. 14 | func LoadFile(filename string) (*Config, error) { 15 | content, err := utils.FileOpen(&filename) 16 | if err != nil { 17 | return nil, err 18 | } 19 | cfg, err := Load(string(content)) 20 | if err != nil { 21 | return nil, err 22 | } 23 | cfg.Configpath = filename 24 | return cfg, nil 25 | } 26 | 27 | func StoreFile(fileName string, c *Config) error { 28 | buf, err := yaml.Marshal(c) 29 | if err != nil { 30 | return errors.WithStack(err) 31 | } 32 | err = ioutil.WriteFile(fileName, buf, os.ModeExclusive) 33 | if err != nil { 34 | return errors.WithStack(err) 35 | } 36 | return nil 37 | } 38 | 39 | // Load parses the YAML input s into a Config. 40 | func Load(s string) (*Config, error) { 41 | cfg := &Config{} 42 | err := yaml.Unmarshal([]byte(s), cfg) 43 | if err != nil { 44 | return nil, err 45 | } 46 | cfg.Original = s 47 | return cfg, nil 48 | } 49 | 50 | // Config is the top-level configuration 51 | type Config struct { 52 | sync.Mutex 53 | InternalConfig InternalConfig `yaml:"internal,omitempty"` 54 | Setting SettingConfig `yaml:"settings,omitempty"` 55 | Original string 56 | Configpath string 57 | } 58 | 59 | type InternalConfig struct { 60 | LogFile string `yaml:"logfile,omitempty"` 61 | Development bool `yaml:"development,omitempty"` 62 | Devices []string `yaml:"devices,omitempty"` 63 | } 64 | 65 | type SettingConfig struct { 66 | Functions []FunctionsConfig `yaml:"functions,omitempty"` 67 | Transitv4 []Transitv4Config `yaml:"transitv4,omitempty"` 68 | Transitv6 []Transitv6Config `yaml:"transitv6,omitempty"` 69 | } 70 | 71 | type FunctionsConfig struct { 72 | Action string `yaml:"action,omitempty"` 73 | TriggerAddr string `yaml:"triggerAddr,omitempty"` 74 | SAddr string `yaml:"actionSrcAddr,omitempty"` 75 | DAddr string `yaml:"actionDstAddr,omitempty"` 76 | Nexthop string `yaml:"nexthop,omitempty"` 77 | Flaver string `yaml:"flaver,omitempty"` 78 | V4AddrSPos string `yaml:"v4AddrSPos,omitempty"` 79 | V4AddrDPos string `yaml:"v4AddrDPos,omitempty"` 80 | } 81 | 82 | type Transitv4Config struct { 83 | Action string `yaml:"action,omitempty"` 84 | TriggerAddr string `yaml:"triggerAddr,omitempty"` 85 | SAddr string `yaml:"actionSrcAddr,omitempty"` 86 | DAddr string `yaml:"actionDstAddr,omitempty"` 87 | Segments []string `yaml:"segments,omitempty"` 88 | } 89 | 90 | type Transitv6Config struct { 91 | Action string `yaml:"action,omitempty"` 92 | TriggerAddr string `yaml:"triggerAddr,omitempty"` 93 | SAddr string `yaml:"actionSrcAddr,omitempty"` 94 | DAddr string `yaml:"actionDstAddr,omitempty"` 95 | Segments []string `yaml:"segments,omitempty"` 96 | } 97 | 98 | // Preset params. 99 | var ( 100 | PresetConfig = Config{ 101 | InternalConfig: PresetInternalConfig, 102 | } 103 | PresetInternalConfig = InternalConfig{ 104 | LogFile: "/var/log/gop4d.log", 105 | Development: false, 106 | } 107 | ) 108 | 109 | func BuildPresetConfig() *Config { 110 | return &PresetConfig 111 | } 112 | -------------------------------------------------------------------------------- /pkg/config/validation.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/pkg/errors" 7 | "github.com/takehaya/vinbero/pkg/srv6" 8 | "github.com/takehaya/vinbero/pkg/utils" 9 | ) 10 | 11 | func (c *Config) Validate() error { 12 | var err error 13 | err = c.InternalConfig.Validate() 14 | if err != nil { 15 | return errors.WithMessage(err, "failed Internal Config") 16 | } 17 | 18 | err = c.Setting.Validate() 19 | if err != nil { 20 | return errors.WithMessage(err, "failed Setting Config") 21 | } 22 | return nil 23 | } 24 | 25 | func (c *InternalConfig) Validate() error { 26 | if len(c.Devices) <= 0 { 27 | return fmt.Errorf("Device is not registered\n") 28 | } 29 | return nil 30 | } 31 | 32 | func (c *SettingConfig) Validate() error { 33 | for _, fn := range c.Functions { 34 | err := fn.Validate() 35 | if err != nil { 36 | return errors.WithMessage(err, "failed Function Config") 37 | } 38 | } 39 | for _, t4 := range c.Transitv4 { 40 | err := t4.Validate() 41 | if err != nil { 42 | return errors.WithMessage(err, "failed Function Config") 43 | } 44 | } 45 | return nil 46 | } 47 | 48 | func (c *FunctionsConfig) Validate() error { 49 | actId, err := srv6.Seg6LocalActionInt(c.Action) 50 | if err != nil { 51 | return errors.WithMessage(err, fmt.Sprintf("%v not found", c.Action)) 52 | } 53 | 54 | if c.TriggerAddr == "" { 55 | return fmt.Errorf("TriggerAddr not found") 56 | } 57 | 58 | checkSaddr := []int{ 59 | srv6.SEG6_LOCAL_ACTION_END_M_GTP6_E, 60 | srv6.SEG6_LOCAL_ACTION_END_M_GTP4_E, 61 | } 62 | if utils.IntArrayContains(checkSaddr, int(actId)) && c.SAddr == "" { 63 | return fmt.Errorf("actionSrcAddr not found") 64 | } else if !utils.IntArrayContains(checkSaddr, int(actId)) && c.SAddr != "" { 65 | return fmt.Errorf("Do not throw in invalid configurations.Is actionSrcAddr") 66 | } 67 | 68 | checkDaddr := []int{} 69 | if utils.IntArrayContains(checkDaddr, int(actId)) && c.DAddr == "" { 70 | return fmt.Errorf("actionDstAddr not found") 71 | } else if !utils.IntArrayContains(checkDaddr, int(actId)) && c.DAddr != "" { 72 | return fmt.Errorf("Do not throw in invalid configurations.Is actionDstAddr") 73 | } 74 | 75 | checkNexthop := []int{ 76 | srv6.SEG6_LOCAL_ACTION_END_DX6, 77 | srv6.SEG6_LOCAL_ACTION_END_DX4, 78 | } 79 | if utils.IntArrayContains(checkNexthop, int(actId)) && c.Nexthop == "" { 80 | return fmt.Errorf("Nexthop not found") 81 | } else if !utils.IntArrayContains(checkNexthop, int(actId)) && c.Nexthop != "" { 82 | return fmt.Errorf("Do not throw in invalid configurations.Is Nexthop") 83 | } 84 | 85 | checkFlaver := []int{ 86 | srv6.SEG6_LOCAL_ACTION_END, 87 | srv6.SEG6_LOCAL_ACTION_END_X, 88 | srv6.SEG6_LOCAL_ACTION_END_T, 89 | } 90 | if utils.IntArrayContains(checkFlaver, int(actId)) && c.Flaver == "" { 91 | return fmt.Errorf("checkFlaver not found") 92 | } else if !utils.IntArrayContains(checkNexthop, int(actId)) && c.Nexthop != "" { 93 | return fmt.Errorf("Do not throw in invalid configurations.Is checkFlaver") 94 | } 95 | 96 | checkV4AddrPos := []int{ 97 | srv6.SEG6_LOCAL_ACTION_END_M_GTP4_E, 98 | } 99 | if utils.IntArrayContains(checkV4AddrPos, int(actId)) && c.V4AddrSPos == "" { 100 | return fmt.Errorf("v4AddrSPos not found") 101 | } else if !utils.IntArrayContains(checkV4AddrPos, int(actId)) && c.V4AddrSPos != "" { 102 | return fmt.Errorf("Do not throw in invalid configurations.Is v4AddrSPos") 103 | } else if utils.IntArrayContains(checkV4AddrPos, int(actId)) && c.V4AddrDPos == "" { 104 | return fmt.Errorf("V4AddrDPos not found") 105 | } else if !utils.IntArrayContains(checkV4AddrPos, int(actId)) && c.V4AddrDPos != "" { 106 | return fmt.Errorf("Do not throw in invalid configurations.Is V4AddrDPos") 107 | } 108 | 109 | return nil 110 | } 111 | 112 | func (c *Transitv4Config) Validate() error { 113 | actId, err := srv6.Seg6EncapModeInt(c.Action) 114 | if err != nil { 115 | return errors.WithMessage(err, fmt.Sprintf("%v not found", c.Action)) 116 | } 117 | if c.TriggerAddr == "" { 118 | return fmt.Errorf("TriggerAddr not found") 119 | } 120 | 121 | checkSaddr := []int{ 122 | srv6.SEG6_IPTUN_MODE_ENCAP, 123 | srv6.SEG6_IPTUN_MODE_ENCAP_H_M_GTP4_D, 124 | } 125 | if utils.IntArrayContains(checkSaddr, int(actId)) && c.SAddr == "" { 126 | return fmt.Errorf("actionSrcAddr not found") 127 | } 128 | 129 | checkDaddr := []int{ 130 | srv6.SEG6_IPTUN_MODE_ENCAP_H_M_GTP4_D, 131 | } 132 | if utils.IntArrayContains(checkDaddr, int(actId)) && c.DAddr == "" { 133 | return fmt.Errorf("actionDstAddr not found") 134 | } 135 | 136 | checkSegments := []int{ 137 | srv6.SEG6_IPTUN_MODE_ENCAP, 138 | } 139 | if utils.IntArrayContains(checkSegments, int(actId)) && len(c.Segments) == 0 { 140 | return fmt.Errorf("Segments not found") 141 | } 142 | 143 | if srv6.MAX_SEGMENTS < len(c.Segments) { 144 | return fmt.Errorf("Max Segments Entry over. %v/%v", len(c.Segments), srv6.MAX_SEGMENTS) 145 | } else if len(c.Segments) == 0 && actId != srv6.SEG6_IPTUN_MODE_ENCAP_H_M_GTP4_D { 146 | return fmt.Errorf("Length Entry empty. %v/%v", c.Segments, srv6.MAX_SEGMENTS) 147 | } 148 | 149 | // reject 150 | if actId == srv6.SEG6_IPTUN_MODE_ENCAP_H_M_GTP4_D { 151 | if srv6.MAX_SEGMENTS-1 < len(c.Segments) { 152 | return fmt.Errorf("Max Segments Entry over. SEG6_IPTUN_MODE_ENCAP_H_M_GTP4_D is maxsize(%v) %v", srv6.MAX_SEGMENTS-1, len(c.Segments)) 153 | } 154 | } 155 | 156 | return nil 157 | } 158 | 159 | func (c *Transitv6Config) Validate() error { 160 | actId, err := srv6.Seg6EncapModeInt(c.Action) 161 | if err != nil { 162 | return errors.WithMessage(err, fmt.Sprintf("%v not found", c.Action)) 163 | } 164 | if c.TriggerAddr == "" { 165 | return fmt.Errorf("TriggerAddr not found") 166 | } 167 | 168 | checkSaddr := []int{ 169 | srv6.SEG6_IPTUN_MODE_ENCAP, 170 | srv6.SEG6_IPTUN_MODE_ENCAP_T_M_GTP6_D, 171 | } 172 | if utils.IntArrayContains(checkSaddr, int(actId)) && c.SAddr == "" { 173 | return fmt.Errorf("actionSrcAddr not found") 174 | } 175 | 176 | checkDaddr := []int{ 177 | srv6.SEG6_IPTUN_MODE_ENCAP_T_M_GTP6_D, 178 | } 179 | if utils.IntArrayContains(checkDaddr, int(actId)) && c.DAddr == "" { 180 | return fmt.Errorf("actionDstAddr not found") 181 | } 182 | 183 | checkSegments := []int{ 184 | srv6.SEG6_IPTUN_MODE_ENCAP, 185 | } 186 | if utils.IntArrayContains(checkSegments, int(actId)) && c.SAddr == "" { 187 | return fmt.Errorf("Segments not found") 188 | } 189 | 190 | if srv6.MAX_SEGMENTS < len(c.Segments) { 191 | return fmt.Errorf("Max Segments Entry over. %v/%v", len(c.Segments), srv6.MAX_SEGMENTS) 192 | } else if len(c.Segments) == 0 && 193 | actId != srv6.SEG6_IPTUN_MODE_ENCAP_T_M_GTP6_D && 194 | actId != srv6.SEG6_IPTUN_MODE_ENCAP_T_M_GTP6_D_Di { 195 | return fmt.Errorf("Length Entry empty. %v/%v", c.Segments, srv6.MAX_SEGMENTS) 196 | } 197 | return nil 198 | } 199 | -------------------------------------------------------------------------------- /pkg/coreelf/elf.go: -------------------------------------------------------------------------------- 1 | package coreelf 2 | 3 | import ( 4 | "github.com/cilium/ebpf" 5 | "github.com/pkg/errors" 6 | ) 7 | 8 | //go:generate go run github.com/cilium/ebpf/cmd/bpf2go -cc clang-11 -target bpf srv6 ../../src/srv6.c -- -I./../../include -I./../../src -Wno-unused-value -Wno-pointer-sign -Wno-compare-distinct-pointer-types -Wnull-character -g -c -O2 -D__KERNEL__ 9 | 10 | func ReadCollection() (*srv6Objects, error) { 11 | spec, err := newSrv6Specs() 12 | if err != nil { 13 | return nil, errors.WithStack(err) 14 | } 15 | 16 | // TODO: BPF log level remove hardcoding. yaml in config 17 | obj, err := spec.Load( 18 | &ebpf.CollectionOptions{ 19 | Programs: ebpf.ProgramOptions{ 20 | LogLevel: 2, 21 | LogSize: 102400 * 1024, 22 | }, 23 | }, 24 | ) 25 | if err != nil { 26 | return nil, errors.WithStack(err) 27 | } 28 | 29 | return obj, nil 30 | } 31 | -------------------------------------------------------------------------------- /pkg/srv6/consts.go: -------------------------------------------------------------------------------- 1 | package srv6 2 | 3 | const MAX_SEGMENTS = 5 4 | -------------------------------------------------------------------------------- /pkg/srv6/function.go: -------------------------------------------------------------------------------- 1 | package srv6 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "unsafe" 7 | 8 | "github.com/pkg/errors" 9 | 10 | "github.com/cilium/ebpf" 11 | "github.com/takehaya/vinbero/pkg/xdptool" 12 | ) 13 | 14 | type FunctionTableKey struct { 15 | Prefixlen uint32 16 | Daddr [16]uint8 17 | } 18 | 19 | type FunctionTable struct { 20 | StartSaddr [16]uint8 21 | Nexthop [16]uint8 22 | Function uint32 23 | Flaver uint32 24 | V4AddrSPos uint32 25 | V4AddrDPos uint32 26 | } 27 | 28 | type FunctionTablesMap struct { 29 | FD int 30 | Map *ebpf.Map 31 | } 32 | 33 | func NewFunctionTable(coll *ebpf.Collection) (*FunctionTablesMap, error) { 34 | m, ok := coll.Maps[STR_FunctionTable] 35 | if !ok { 36 | return nil, errors.New(fmt.Sprintf("%v not found", STR_FunctionTable)) 37 | } 38 | return &FunctionTablesMap{ 39 | FD: m.FD(), 40 | Map: m, 41 | }, nil 42 | } 43 | 44 | func MappingFunctionTable(m *ebpf.Map) *FunctionTablesMap { 45 | return &FunctionTablesMap{ 46 | FD: m.FD(), 47 | Map: m, 48 | } 49 | } 50 | 51 | func (m *FunctionTablesMap) Update(fn FunctionTable, ip [16]byte, prefix uint32) error { 52 | key := FunctionTableKey{ 53 | Daddr: ip, 54 | Prefixlen: prefix, 55 | } 56 | if err := m.Map.Put(key, fn); err != nil { 57 | return errors.WithMessage(err, "Can't put function table map") 58 | } 59 | return nil 60 | } 61 | 62 | func (m *FunctionTablesMap) Get(ip [16]byte, prefix uint32) (*FunctionTable, error) { 63 | key := FunctionTableKey{ 64 | Daddr: ip, 65 | Prefixlen: prefix, 66 | } 67 | entry := make([]FunctionTable, xdptool.PossibleCpus) 68 | err := xdptool.LookupElement(m.FD, unsafe.Pointer(&key), unsafe.Pointer(&entry[0])) 69 | if err != nil { 70 | return nil, err 71 | } 72 | return &entry[0], nil 73 | } 74 | 75 | func (m *FunctionTablesMap) Delete(ip [16]byte, prefix uint32) error { 76 | key := FunctionTableKey{ 77 | Daddr: ip, 78 | Prefixlen: prefix, 79 | } 80 | return xdptool.DeleteElement(m.FD, unsafe.Pointer(&key)) 81 | } 82 | 83 | func (m *FunctionTablesMap) List() ([]*FunctionTable, error) { 84 | functables := []*FunctionTable{} 85 | var key, nextKey FunctionTableKey 86 | for { 87 | entry := make([]FunctionTable, xdptool.PossibleCpus) 88 | err := xdptool.GetNextKey(m.FD, unsafe.Pointer(&key), unsafe.Pointer(&nextKey)) 89 | if err != nil { 90 | break 91 | } 92 | err = xdptool.LookupElement(m.FD, unsafe.Pointer(&nextKey), unsafe.Pointer(&entry[0])) 93 | if err != nil { 94 | return nil, fmt.Errorf("unable to lookup %s map: %s", STR_FunctionTable, err) 95 | } 96 | functables = append(functables, &entry[0]) 97 | key = nextKey 98 | } 99 | return functables, nil 100 | } 101 | 102 | func (m *FunctionTablesMap) Pin() error { 103 | return xdptool.ObjPin(m.FD, FunctionTablePath) 104 | } 105 | 106 | func (m *FunctionTablesMap) Unpin() error { 107 | return os.Remove(FunctionTablePath) 108 | } 109 | 110 | func LoadFunctionTables() (*FunctionTablesMap, error) { 111 | m, err := ebpf.LoadPinnedMap(FunctionTablePath) 112 | if err != nil { 113 | return nil, err 114 | } 115 | return &FunctionTablesMap{FD: m.FD(), Map: m}, nil 116 | } 117 | -------------------------------------------------------------------------------- /pkg/srv6/maps.go: -------------------------------------------------------------------------------- 1 | package srv6 2 | 3 | import ( 4 | "path" 5 | 6 | "github.com/takehaya/vinbero/pkg/xdptool" 7 | ) 8 | 9 | const ( 10 | STR_TXPort = "tx_port" 11 | STR_TransitTablev4 = "transit_table_v4" 12 | STR_TransitTablev6 = "transit_table_v6" 13 | STR_FunctionTable = "function_table" 14 | STR_V6addrHeep = "v6addr_heep" 15 | ) 16 | 17 | var ( 18 | TXPortPath = path.Join(xdptool.BpfFsPath, STR_TXPort) 19 | TransitTablev4Path = path.Join(xdptool.BpfFsPath, STR_TransitTablev4) 20 | TransitTablev6Path = path.Join(xdptool.BpfFsPath, STR_TransitTablev6) 21 | FunctionTablePath = path.Join(xdptool.BpfFsPath, STR_FunctionTable) 22 | V6addrHeepPath = path.Join(xdptool.BpfFsPath, STR_V6addrHeep) 23 | ) 24 | -------------------------------------------------------------------------------- /pkg/srv6/seg6.go: -------------------------------------------------------------------------------- 1 | // https://github.com/vishvananda/netlink/blob/master/nl/seg6_linux.go 2 | package srv6 3 | 4 | import ( 5 | "fmt" 6 | "net" 7 | ) 8 | 9 | const ( 10 | SEG6_GTPV1_LOC_FUNCTION_MAXSIZE = 56 // == 128 - v4addr(32) - args(40) 11 | ) 12 | 13 | type IPv6SrHdr struct { 14 | nextHdr uint8 15 | hdrLen uint8 16 | routingType uint8 17 | segmentsLeft uint8 18 | firstSegment uint8 19 | flags uint8 20 | reserved uint16 21 | 22 | Segments []net.IP 23 | } 24 | 25 | // seg6 encap mode 26 | const ( 27 | SEG6_IPTUN_MODE_INLINE = iota 28 | SEG6_IPTUN_MODE_ENCAP 29 | SEG6_IPTUN_MODE_L2ENCAP 30 | SEG6_IPTUN_MODE_ENCAP_T_M_GTP6_D 31 | SEG6_IPTUN_MODE_ENCAP_T_M_GTP6_D_Di 32 | SEG6_IPTUN_MODE_ENCAP_H_M_GTP4_D 33 | __SEG6_IPTUN_MODE_MAX 34 | ) 35 | 36 | const ( 37 | SEG6_IPTUN_MODE_MAX = __SEG6_IPTUN_MODE_MAX 38 | ) 39 | 40 | // Helper functions 41 | func Seg6EncapModeString(mode int) (string, error) { 42 | switch mode { 43 | case SEG6_IPTUN_MODE_INLINE: 44 | return "T.Insert", nil 45 | case SEG6_IPTUN_MODE_ENCAP: 46 | return "T.Encaps", nil 47 | case SEG6_IPTUN_MODE_L2ENCAP: 48 | return "T.Encaps.L2", nil 49 | case SEG6_IPTUN_MODE_ENCAP_T_M_GTP6_D: 50 | return "T.M.GTP6.D", nil 51 | case SEG6_IPTUN_MODE_ENCAP_T_M_GTP6_D_Di: 52 | return "T.M.GTP6.D.Di", nil 53 | case SEG6_IPTUN_MODE_ENCAP_H_M_GTP4_D: 54 | return "H.M.GTP4.D", nil 55 | } 56 | return "", fmt.Errorf("%d mode number not match", mode) 57 | } 58 | 59 | func Seg6EncapModeInt(name string) (uint32, error) { 60 | switch name { 61 | case "SEG6_IPTUN_MODE_INLINE": 62 | return SEG6_IPTUN_MODE_INLINE, nil 63 | case "SEG6_IPTUN_MODE_ENCAP": 64 | return SEG6_IPTUN_MODE_ENCAP, nil 65 | case "SEG6_IPTUN_MODE_ENCAP_T_M_GTP6_D": 66 | return SEG6_IPTUN_MODE_ENCAP_T_M_GTP6_D, nil 67 | case "SEG6_IPTUN_MODE_ENCAP_T_M_GTP6_D_Di": 68 | return SEG6_IPTUN_MODE_ENCAP_T_M_GTP6_D_Di, nil 69 | case "SEG6_IPTUN_MODE_ENCAP_H_M_GTP4_D": 70 | return SEG6_IPTUN_MODE_ENCAP_H_M_GTP4_D, nil 71 | } 72 | return 0, fmt.Errorf("%d action not match", name) 73 | } 74 | -------------------------------------------------------------------------------- /pkg/srv6/seg6_local.go: -------------------------------------------------------------------------------- 1 | // https://github.com/vishvananda/netlink/blob/master/nl/seg6local_linux.go 2 | package srv6 3 | 4 | import "fmt" 5 | 6 | // seg6local parameters 7 | const ( 8 | SEG6_LOCAL_UNSPEC = iota 9 | SEG6_LOCAL_ACTION 10 | SEG6_LOCAL_SRH 11 | SEG6_LOCAL_TABLE 12 | SEG6_LOCAL_NH4 13 | SEG6_LOCAL_NH6 14 | SEG6_LOCAL_IIF 15 | SEG6_LOCAL_OIF 16 | __SEG6_LOCAL_MAX 17 | ) 18 | const ( 19 | SEG6_LOCAL_MAX = __SEG6_LOCAL_MAX 20 | ) 21 | 22 | // seg6End Functions Flaver 23 | const ( 24 | SEG6_LOCAL_FLAVER_NONE = iota + 1 // 1 25 | SEG6_LOCAL_FLAVER_PSP 26 | SEG6_LOCAL_FLAVER_USP 27 | SEG6_LOCAL_FLAVER_USD 28 | __SEG6_LOCAL_FLAVER_MAX 29 | ) 30 | const ( 31 | SEG6_LOCAL_FLAVER_MAX = __SEG6_LOCAL_FLAVER_MAX 32 | ) 33 | 34 | // Helper functions 35 | func Seg6LocalFlaverString(action int) (string, error) { 36 | switch action { 37 | case SEG6_LOCAL_FLAVER_NONE: 38 | return "NONE", nil 39 | case SEG6_LOCAL_FLAVER_PSP: 40 | return "PSP", nil 41 | case SEG6_LOCAL_FLAVER_USP: 42 | return "USP", nil 43 | case SEG6_LOCAL_FLAVER_USD: 44 | return "USD", nil 45 | } 46 | 47 | return "", fmt.Errorf("%d action number not match", action) 48 | } 49 | 50 | // Helper functions 51 | func Seg6LocalFlaverInt(name string) (uint32, error) { 52 | switch name { 53 | case "NONE": 54 | return SEG6_LOCAL_FLAVER_NONE, nil 55 | case "PSP": 56 | return SEG6_LOCAL_FLAVER_PSP, nil 57 | case "USP": 58 | return SEG6_LOCAL_FLAVER_USP, nil 59 | case "USD": 60 | return SEG6_LOCAL_FLAVER_USD, nil 61 | } 62 | 63 | return 0, fmt.Errorf("%d action not match", name) 64 | } 65 | 66 | // seg6local actions 67 | const ( 68 | SEG6_LOCAL_ACTION_END = iota + 1 // 1 69 | SEG6_LOCAL_ACTION_END_X // 2 70 | SEG6_LOCAL_ACTION_END_T // 3 71 | SEG6_LOCAL_ACTION_END_DX2 // 4 72 | SEG6_LOCAL_ACTION_END_DX6 // 5 73 | SEG6_LOCAL_ACTION_END_DX4 // 6 74 | SEG6_LOCAL_ACTION_END_DT6 // 7 75 | SEG6_LOCAL_ACTION_END_DT4 // 8 76 | SEG6_LOCAL_ACTION_END_B6 // 9 77 | SEG6_LOCAL_ACTION_END_B6_ENCAPS // 10 78 | SEG6_LOCAL_ACTION_END_BM // 11 79 | SEG6_LOCAL_ACTION_END_S // 12 80 | SEG6_LOCAL_ACTION_END_AS // 13 81 | SEG6_LOCAL_ACTION_END_AM // 14 82 | SEG6_LOCAL_ACTION_END_M_GTP6_E 83 | SEG6_LOCAL_ACTION_END_M_GTP4_E 84 | 85 | __SEG6_LOCAL_ACTION_MAX 86 | ) 87 | const ( 88 | SEG6_LOCAL_ACTION_MAX = __SEG6_LOCAL_ACTION_MAX - 1 89 | ) 90 | 91 | // Helper functions 92 | func Seg6LocalActionString(action int) (string, error) { 93 | switch action { 94 | case SEG6_LOCAL_ACTION_END: 95 | return "End", nil 96 | case SEG6_LOCAL_ACTION_END_X: 97 | return "End.X", nil 98 | case SEG6_LOCAL_ACTION_END_T: 99 | return "End.T", nil 100 | case SEG6_LOCAL_ACTION_END_DX2: 101 | return "End.DX2", nil 102 | case SEG6_LOCAL_ACTION_END_DX6: 103 | return "End.DX6", nil 104 | case SEG6_LOCAL_ACTION_END_DX4: 105 | return "End.DX4", nil 106 | case SEG6_LOCAL_ACTION_END_DT6: 107 | return "End.DT6", nil 108 | case SEG6_LOCAL_ACTION_END_DT4: 109 | return "End.DT4", nil 110 | case SEG6_LOCAL_ACTION_END_B6: 111 | return "End.B6", nil 112 | case SEG6_LOCAL_ACTION_END_B6_ENCAPS: 113 | return "End.B6.Encaps", nil 114 | case SEG6_LOCAL_ACTION_END_BM: 115 | return "End.BM", nil 116 | case SEG6_LOCAL_ACTION_END_S: 117 | return "End.S", nil 118 | case SEG6_LOCAL_ACTION_END_AS: 119 | return "End.AS", nil 120 | case SEG6_LOCAL_ACTION_END_AM: 121 | return "End.AM", nil 122 | case SEG6_LOCAL_ACTION_END_M_GTP6_E: 123 | return "End.M.GTP6.E", nil 124 | case SEG6_LOCAL_ACTION_END_M_GTP4_E: 125 | return "End.M.GTP4.E", nil 126 | } 127 | return "", fmt.Errorf("%d action number not match", action) 128 | } 129 | 130 | // Helper functions 131 | func Seg6LocalActionInt(name string) (uint32, error) { 132 | switch name { 133 | case "SEG6_LOCAL_ACTION_END": 134 | return SEG6_LOCAL_ACTION_END, nil 135 | case "SEG6_LOCAL_ACTION_END_X": 136 | return SEG6_LOCAL_ACTION_END_X, nil 137 | case "SEG6_LOCAL_ACTION_END_T": 138 | return SEG6_LOCAL_ACTION_END_T, nil 139 | case "SEG6_LOCAL_ACTION_END_DX2": 140 | return SEG6_LOCAL_ACTION_END_DX2, nil 141 | case "SEG6_LOCAL_ACTION_END_DX6": 142 | return SEG6_LOCAL_ACTION_END_DX6, nil 143 | case "SEG6_LOCAL_ACTION_END_DX4": 144 | return SEG6_LOCAL_ACTION_END_DX4, nil 145 | case "SEG6_LOCAL_ACTION_END_DT6": 146 | return SEG6_LOCAL_ACTION_END_DT6, nil 147 | case "SEG6_LOCAL_ACTION_END_DT4": 148 | return SEG6_LOCAL_ACTION_END_DT4, nil 149 | case "SEG6_LOCAL_ACTION_END_B6": 150 | return SEG6_LOCAL_ACTION_END_B6, nil 151 | case "SEG6_LOCAL_ACTION_END_B6_ENCAPS": 152 | return SEG6_LOCAL_ACTION_END_B6_ENCAPS, nil 153 | case "SEG6_LOCAL_ACTION_END_BM": 154 | return SEG6_LOCAL_ACTION_END_BM, nil 155 | case "SEG6_LOCAL_ACTION_END_S": 156 | return SEG6_LOCAL_ACTION_END_S, nil 157 | case "SEG6_LOCAL_ACTION_END_AS": 158 | return SEG6_LOCAL_ACTION_END_AS, nil 159 | case "SEG6_LOCAL_ACTION_END_AM": 160 | return SEG6_LOCAL_ACTION_END_AM, nil 161 | case "SEG6_LOCAL_ACTION_END_M_GTP6_E": 162 | return SEG6_LOCAL_ACTION_END_M_GTP6_E, nil 163 | case "SEG6_LOCAL_ACTION_END_M_GTP4_E": 164 | return SEG6_LOCAL_ACTION_END_M_GTP4_E, nil 165 | } 166 | return 0, fmt.Errorf("%s action not match", name) 167 | } 168 | -------------------------------------------------------------------------------- /pkg/srv6/transit_tablev4.go: -------------------------------------------------------------------------------- 1 | package srv6 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | "github.com/pkg/errors" 8 | 9 | "github.com/cilium/ebpf" 10 | "github.com/takehaya/vinbero/pkg/xdptool" 11 | ) 12 | 13 | type TransitTablev4Key struct { 14 | Prefixlen uint32 15 | Daddr [4]byte 16 | } 17 | 18 | type TransitTablev4 struct { 19 | Saddr [16]byte 20 | Daddr [16]byte 21 | SPrefixlen uint32 22 | DPrefixlen uint32 23 | SegmentLength uint32 24 | Action uint32 25 | Segments [MAX_SEGMENTS][16]byte 26 | } 27 | 28 | type TransitTablev4sMap struct { 29 | FD int 30 | Map *ebpf.Map 31 | } 32 | 33 | func NewTransitTablev4(coll *ebpf.Collection) (*TransitTablev4sMap, error) { 34 | m, ok := coll.Maps[STR_TransitTablev4] 35 | if !ok { 36 | return nil, errors.New(fmt.Sprintf("%v not found", STR_TransitTablev4)) 37 | } 38 | return &TransitTablev4sMap{ 39 | FD: m.FD(), 40 | Map: m, 41 | }, nil 42 | } 43 | 44 | func MappingTransitTablev4(m *ebpf.Map) *TransitTablev4sMap { 45 | return &TransitTablev4sMap{ 46 | FD: m.FD(), 47 | Map: m, 48 | } 49 | } 50 | 51 | func (m *TransitTablev4sMap) Update(v4table TransitTablev4, ip [4]byte, prefix uint32) error { 52 | key := TransitTablev4Key{ 53 | Daddr: ip, 54 | Prefixlen: prefix, 55 | } 56 | if err := m.Map.Put(key, v4table); err != nil { 57 | return errors.WithMessage(err, "Can't put function table map") 58 | } 59 | return nil 60 | } 61 | 62 | func (m *TransitTablev4sMap) Pin() error { 63 | return xdptool.ObjPin(m.FD, TransitTablev4Path) 64 | } 65 | 66 | func (m *TransitTablev4sMap) Unpin() error { 67 | return os.Remove(TransitTablev4Path) 68 | } 69 | 70 | func LoadTransitTablev4() (*TransitTablev4sMap, error) { 71 | m, err := ebpf.LoadPinnedMap(TransitTablev4Path) 72 | if err != nil { 73 | return nil, err 74 | } 75 | return &TransitTablev4sMap{FD: m.FD(), Map: m}, nil 76 | } 77 | -------------------------------------------------------------------------------- /pkg/srv6/transit_tablev6.go: -------------------------------------------------------------------------------- 1 | package srv6 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | "github.com/pkg/errors" 8 | 9 | "github.com/cilium/ebpf" 10 | "github.com/takehaya/vinbero/pkg/xdptool" 11 | ) 12 | 13 | type TransitTablev6Key struct { 14 | Prefixlen uint32 15 | Daddr [16]byte 16 | } 17 | 18 | type TransitTablev6 struct { 19 | Saddr [16]byte 20 | Daddr [16]byte 21 | SPrefixlen uint32 22 | DPrefixlen uint32 23 | SegmentLength uint32 24 | Action uint32 25 | Segments [MAX_SEGMENTS][16]byte 26 | } 27 | 28 | type TransitTablev6sMap struct { 29 | FD int 30 | Map *ebpf.Map 31 | } 32 | 33 | func NewTransitTablev6(coll *ebpf.Collection) (*TransitTablev6sMap, error) { 34 | m, ok := coll.Maps[STR_TransitTablev6] 35 | if !ok { 36 | return nil, errors.New(fmt.Sprintf("%v not found", STR_TransitTablev4)) 37 | } 38 | return &TransitTablev6sMap{ 39 | FD: m.FD(), 40 | Map: m, 41 | }, nil 42 | } 43 | 44 | func MappingTransitTablev6(m *ebpf.Map) *TransitTablev6sMap { 45 | return &TransitTablev6sMap{ 46 | FD: m.FD(), 47 | Map: m, 48 | } 49 | } 50 | 51 | func (m *TransitTablev6sMap) Update(v4table TransitTablev6, ip [16]byte, prefix uint32) error { 52 | key := TransitTablev6Key{ 53 | Daddr: ip, 54 | Prefixlen: prefix, 55 | } 56 | if err := m.Map.Put(key, v4table); err != nil { 57 | return errors.WithMessage(err, "Can't put function table map") 58 | } 59 | return nil 60 | } 61 | 62 | func (m *TransitTablev6sMap) Pin() error { 63 | return xdptool.ObjPin(m.FD, TransitTablev6Path) 64 | } 65 | 66 | func (m *TransitTablev6sMap) Unpin() error { 67 | return os.Remove(TransitTablev6Path) 68 | } 69 | 70 | func LoadTransitTablev6() (*TransitTablev6sMap, error) { 71 | m, err := ebpf.LoadPinnedMap(TransitTablev6Path) 72 | if err != nil { 73 | return nil, err 74 | } 75 | return &TransitTablev6sMap{FD: m.FD(), Map: m}, nil 76 | } 77 | -------------------------------------------------------------------------------- /pkg/srv6/tx_port.go: -------------------------------------------------------------------------------- 1 | package srv6 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "os" 7 | "unsafe" 8 | 9 | "github.com/pkg/errors" 10 | 11 | "github.com/cilium/ebpf" 12 | "github.com/takehaya/vinbero/pkg/xdptool" 13 | ) 14 | 15 | const MaxTxportDevice = 64 16 | 17 | type TxPortKey struct { 18 | Iface uint32 19 | } 20 | 21 | type TxPort struct { 22 | Iface uint32 23 | } 24 | 25 | type TxPortsMap struct { 26 | FD int 27 | Map *ebpf.Map 28 | } 29 | 30 | func NewTxPort(coll *ebpf.Collection) (*TxPortsMap, error) { 31 | log.Println("%v", coll) 32 | m, ok := coll.Maps[STR_TXPort] 33 | if !ok { 34 | return nil, errors.New(fmt.Sprintf("%v not found", STR_TXPort)) 35 | } 36 | return &TxPortsMap{ 37 | FD: m.FD(), 38 | Map: m, 39 | }, nil 40 | } 41 | 42 | func MappingTxPort(m *ebpf.Map) *TxPortsMap { 43 | return &TxPortsMap{ 44 | FD: m.FD(), 45 | Map: m, 46 | } 47 | } 48 | 49 | func (m *TxPortsMap) Update(txp TxPort, iface int) error { 50 | key := TxPortKey{Iface: uint32(iface)} 51 | 52 | if err := m.Map.Put(key, txp); err != nil { 53 | return errors.WithMessage(err, "Can't put function table map") 54 | } 55 | return nil 56 | } 57 | 58 | func (m *TxPortsMap) Get(iface int) (*TxPort, error) { 59 | key := TxPortKey{Iface: uint32(iface)} 60 | entry := make([]TxPort, xdptool.PossibleCpus) 61 | err := xdptool.LookupElement(m.FD, unsafe.Pointer(&key), unsafe.Pointer(&entry[0])) 62 | if err != nil { 63 | return nil, err 64 | } 65 | return &entry[0], nil 66 | } 67 | 68 | func (m *TxPortsMap) Pin() error { 69 | return xdptool.ObjPin(m.FD, TXPortPath) 70 | } 71 | 72 | func (m *TxPortsMap) Unpin() error { 73 | return os.Remove(TXPortPath) 74 | } 75 | 76 | func LoadTxPort() (*TxPortsMap, error) { 77 | m, err := ebpf.LoadPinnedMap(TXPortPath) 78 | if err != nil { 79 | return nil, err 80 | } 81 | return &TxPortsMap{FD: m.FD(), Map: m}, nil 82 | } 83 | -------------------------------------------------------------------------------- /pkg/srv6/v6addrheep.go: -------------------------------------------------------------------------------- 1 | package srv6 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "os" 7 | 8 | "github.com/pkg/errors" 9 | 10 | "github.com/cilium/ebpf" 11 | "github.com/takehaya/vinbero/pkg/xdptool" 12 | ) 13 | 14 | type V6addrHeep struct { 15 | Saddr [16]byte 16 | Daddr [16]byte 17 | } 18 | 19 | type V6addrHeepMap struct { 20 | FD int 21 | Map *ebpf.Map 22 | } 23 | 24 | func NewV6addrHeep(coll *ebpf.Collection) (*V6addrHeepMap, error) { 25 | log.Println("%v", coll) 26 | m, ok := coll.Maps[STR_V6addrHeep] 27 | if !ok { 28 | return nil, errors.New(fmt.Sprintf("%v not found", STR_V6addrHeep)) 29 | } 30 | return &V6addrHeepMap{ 31 | FD: m.FD(), 32 | Map: m, 33 | }, nil 34 | } 35 | 36 | func MappingV6addrHeep(m *ebpf.Map) *V6addrHeepMap { 37 | return &V6addrHeepMap{ 38 | FD: m.FD(), 39 | Map: m, 40 | } 41 | } 42 | 43 | func (m *V6addrHeepMap) Update(heep []*V6addrHeep, vkey int) error { 44 | 45 | if err := m.Map.Put(uint32(vkey), heep); err != nil { 46 | return errors.WithMessage(err, "Can't put v6addr map") 47 | } 48 | return nil 49 | } 50 | 51 | func (m *V6addrHeepMap) Pin() error { 52 | return xdptool.ObjPin(m.FD, TXPortPath) 53 | } 54 | 55 | func (m *V6addrHeepMap) Unpin() error { 56 | return os.Remove(TXPortPath) 57 | } 58 | -------------------------------------------------------------------------------- /pkg/utils/array.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | func IntArrayContains(s []int, e int) bool { 4 | for _, a := range s { 5 | if a == e { 6 | return true 7 | } 8 | } 9 | return false 10 | } 11 | -------------------------------------------------------------------------------- /pkg/utils/file.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "io/ioutil" 5 | "os" 6 | 7 | "github.com/pkg/errors" 8 | ) 9 | 10 | func FileOpen(filepath *string) ([]byte, error) { 11 | buf, err := ioutil.ReadFile(*filepath) 12 | if err != nil { 13 | return nil, errors.WithStack(err) 14 | } 15 | return buf, nil 16 | } 17 | 18 | func FileExists(filename string) bool { 19 | _, err := os.Stat(filename) 20 | return err == nil 21 | } 22 | -------------------------------------------------------------------------------- /pkg/version/version.go: -------------------------------------------------------------------------------- 1 | package version 2 | 3 | var Version = "unknown" 4 | var Revision = "unknown" 5 | -------------------------------------------------------------------------------- /pkg/xdptool/apply.go: -------------------------------------------------------------------------------- 1 | package xdptool 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | "github.com/cilium/ebpf" 8 | "github.com/pkg/errors" 9 | "github.com/vishvananda/netlink" 10 | ) 11 | 12 | func LoadElf(filepath string) (*ebpf.Collection, error) { 13 | f, err := os.Open(filepath) 14 | if err != nil { 15 | return nil, errors.WithStack(err) 16 | } 17 | 18 | // Read ELF 19 | spec, err := ebpf.LoadCollectionSpecFromReader(f) 20 | if err != nil { 21 | return nil, errors.WithStack(err) 22 | } 23 | 24 | coll, err := ebpf.NewCollection(spec) 25 | if err != nil { 26 | return nil, errors.WithStack(err) 27 | } 28 | return coll, nil 29 | } 30 | 31 | func Attach(prog *ebpf.Program, device string) error { 32 | link, err := netlink.LinkByName(device) 33 | if err != nil { 34 | return errors.New(fmt.Sprintf("%s not found in object", device)) 35 | } 36 | if err := netlink.LinkSetXdpFd(link, prog.FD()); err != nil { 37 | return errors.WithStack(err) 38 | } 39 | return nil 40 | } 41 | 42 | func Detach(device string) error { 43 | link, err := netlink.LinkByName(device) 44 | if err != nil { 45 | return errors.New(fmt.Sprintf("failed to get device %s: %s\n", device, err)) 46 | } 47 | if err := netlink.LinkSetXdpFd(link, -1); err != nil { 48 | return errors.WithStack(err) 49 | } 50 | return nil 51 | } 52 | -------------------------------------------------------------------------------- /pkg/xdptool/map.go: -------------------------------------------------------------------------------- 1 | package xdptool 2 | 3 | // #include 4 | import "C" 5 | 6 | import ( 7 | "fmt" 8 | "net" 9 | "os" 10 | "strings" 11 | "syscall" 12 | "unsafe" 13 | 14 | "github.com/pkg/errors" 15 | "golang.org/x/sys/unix" 16 | ) 17 | 18 | // See https://github.com/cilium/cilium/blob/master/pkg/bpf/bpf.go 19 | // BPF map type constants. Must match enum bpf_map_type from linux/bpf.h 20 | const ( 21 | BPF_MAP_TYPE_UNSPEC = iota 22 | BPF_MAP_TYPE_HASH 23 | BPF_MAP_TYPE_ARRAY 24 | BPF_MAP_TYPE_PROG_ARRAY 25 | BPF_MAP_TYPE_PERF_EVENT_ARRAY 26 | BPF_MAP_TYPE_PERCPU_HASH 27 | BPF_MAP_TYPE_PERCPU_ARRAY 28 | BPF_MAP_TYPE_STACK_TRACE 29 | BPF_MAP_TYPE_CGROUP_ARRAY 30 | BPF_MAP_TYPE_LRU_HASH 31 | BPF_MAP_TYPE_LRU_PERCPU_HASH 32 | BPF_MAP_TYPE_LPM_TRIE 33 | BPF_MAP_TYPE_ARRAY_OF_MAPS 34 | BPF_MAP_TYPE_HASH_OF_MAPS 35 | BPF_MAP_TYPE_DEVMAP 36 | BPF_MAP_TYPE_SOCKMAP 37 | BPF_MAP_TYPE_CPUMAP 38 | BPF_MAP_TYPE_XSKMAP 39 | BPF_MAP_TYPE_SOCKHASH 40 | BPF_MAP_TYPE_CGROUP_STORAGE 41 | BPF_MAP_TYPE_REUSEPORT_SOCKARRAY 42 | ) 43 | 44 | // BPF syscall command constants. Must match enum bpf_cmd from linux/bpf.h 45 | const ( 46 | BPF_MAP_CREATE = iota 47 | BPF_MAP_LOOKUP_ELEM 48 | BPF_MAP_UPDATE_ELEM 49 | BPF_MAP_DELETE_ELEM 50 | BPF_MAP_GET_NEXT_KEY 51 | BPF_PROG_LOAD 52 | BPF_OBJ_PIN 53 | BPF_OBJ_GET 54 | BPF_PROG_ATTACH 55 | BPF_PROG_DETACH 56 | BPF_PROG_TEST_RUN 57 | BPF_PROG_GET_NEXT_ID 58 | BPF_MAP_GET_NEXT_ID 59 | BPF_PROG_GET_FD_BY_ID 60 | BPF_MAP_GET_FD_BY_ID 61 | BPF_OBJ_GET_INFO_BY_FD 62 | BPF_PROG_QUERY 63 | BPF_RAW_TRACEPOINT_OPEN 64 | BPF_BTF_LOAD 65 | BPF_BTF_GET_FD_BY_ID 66 | BPF_TASK_FD_QUERY 67 | ) 68 | 69 | // Flags for BPF_MAP_UPDATE_ELEM. Must match values from linux/bpf.h 70 | const ( 71 | BPF_ANY = iota 72 | BPF_NOEXIST 73 | BPF_EXIST 74 | ) 75 | 76 | // Flags for BPF_MAP_CREATE. Must match values from linux/bpf.h 77 | const ( 78 | BPF_F_NO_PREALLOC = 1 << 0 79 | BPF_F_NO_COMMON_LRU = 1 << 1 80 | BPF_F_NUMA_NODE = 1 << 2 81 | ) 82 | 83 | // Fd represents HASH_OF_MAPS value. 84 | type Fd struct{ Fd uint32 } 85 | 86 | // This struct must be in sync with union bpf_attr's anonymous struct 87 | // used by the BPF_MAP_CREATE command 88 | type bpfAttrCreateMap struct { 89 | mapType uint32 90 | keySize uint32 91 | valueSize uint32 92 | maxEntries uint32 93 | mapFlags uint32 94 | innerID uint32 95 | } 96 | 97 | func CreateLPMtrieKey(s string) *net.IPNet { 98 | var ipnet *net.IPNet 99 | // Check if given address is CIDR 100 | if strings.Contains(s, "/") { 101 | _, ipnet, _ = net.ParseCIDR(s) 102 | } else { 103 | if strings.Contains(s, ":") { 104 | // IPv6 105 | _, ipnet, _ = net.ParseCIDR(s + "/128") 106 | } else { 107 | // IPv4 108 | _, ipnet, _ = net.ParseCIDR(s + "/32") 109 | } 110 | } 111 | return ipnet 112 | } 113 | 114 | func CreateMap(mapType int, keySize, valueSize, maxEntries, flags, innerID uint32) (int, error) { 115 | uba := bpfAttrCreateMap{ 116 | uint32(mapType), 117 | keySize, 118 | valueSize, 119 | maxEntries, 120 | flags, 121 | innerID, 122 | } 123 | 124 | ret, _, err := unix.Syscall( 125 | unix.SYS_BPF, 126 | BPF_MAP_CREATE, 127 | uintptr(unsafe.Pointer(&uba)), 128 | unsafe.Sizeof(uba), 129 | ) 130 | 131 | if err != 0 { 132 | return 0, fmt.Errorf("Unable to create map: %s", err) 133 | } 134 | return int(ret), nil 135 | } 136 | 137 | type bpfAttrMapOpElem struct { 138 | mapFd uint32 139 | pad0 [4]byte 140 | key uint64 141 | value uint64 // union: value or next_key 142 | flags uint64 143 | } 144 | 145 | func UpdateElement(fd int, key, value unsafe.Pointer, flags uint64) error { 146 | uba := bpfAttrMapOpElem{ 147 | mapFd: uint32(fd), 148 | key: uint64(uintptr(key)), 149 | value: uint64(uintptr(value)), 150 | flags: uint64(flags), 151 | } 152 | 153 | ret, _, err := unix.Syscall( 154 | unix.SYS_BPF, 155 | BPF_MAP_UPDATE_ELEM, 156 | uintptr(unsafe.Pointer(&uba)), 157 | unsafe.Sizeof(uba), 158 | ) 159 | 160 | if ret != 0 || err != 0 { 161 | return errors.New(fmt.Sprintf("Unable to update element for map with file descriptor %d: %s", fd, err)) 162 | } 163 | 164 | return nil 165 | } 166 | 167 | func LookupElement(fd int, key, value unsafe.Pointer) error { 168 | uba := bpfAttrMapOpElem{ 169 | mapFd: uint32(fd), 170 | key: uint64(uintptr(key)), 171 | value: uint64(uintptr(value)), 172 | } 173 | 174 | ret, _, err := unix.Syscall( 175 | unix.SYS_BPF, 176 | BPF_MAP_LOOKUP_ELEM, 177 | uintptr(unsafe.Pointer(&uba)), 178 | unsafe.Sizeof(uba), 179 | ) 180 | 181 | if ret != 0 || err != 0 { 182 | return errors.New(fmt.Sprintf("Unable to lookup element in map with file descriptor %d: %s", fd, err)) 183 | } 184 | 185 | return nil 186 | } 187 | 188 | func deleteElement(fd int, key unsafe.Pointer) (uintptr, syscall.Errno) { 189 | uba := bpfAttrMapOpElem{ 190 | mapFd: uint32(fd), 191 | key: uint64(uintptr(key)), 192 | } 193 | ret, _, err := unix.Syscall( 194 | unix.SYS_BPF, 195 | BPF_MAP_DELETE_ELEM, 196 | uintptr(unsafe.Pointer(&uba)), 197 | unsafe.Sizeof(uba), 198 | ) 199 | 200 | return ret, err 201 | } 202 | 203 | // DeleteElement deletes the map element with the given key. 204 | func DeleteElement(fd int, key unsafe.Pointer) error { 205 | ret, err := deleteElement(fd, key) 206 | 207 | if ret != 0 || err != 0 { 208 | return errors.New(fmt.Sprintf("Unable to lookup element in map with file descriptor %d: %s", fd, err)) 209 | } 210 | 211 | return nil 212 | } 213 | 214 | // GetNextKey stores, in nextKey, the next key after the key of the map in fd. 215 | func GetNextKey(fd int, key, nextKey unsafe.Pointer) error { 216 | uba := bpfAttrMapOpElem{ 217 | mapFd: uint32(fd), 218 | key: uint64(uintptr(key)), 219 | value: uint64(uintptr(nextKey)), 220 | } 221 | ret, _, err := unix.Syscall( 222 | unix.SYS_BPF, 223 | BPF_MAP_GET_NEXT_KEY, 224 | uintptr(unsafe.Pointer(&uba)), 225 | unsafe.Sizeof(uba), 226 | ) 227 | 228 | if ret != 0 || err != 0 { 229 | return errors.New(fmt.Sprintf("Unable to lookup element in map with file descriptor %d: %s", fd, err)) 230 | } 231 | 232 | return nil 233 | } 234 | 235 | const BpfFsPath = "/sys/fs/bpf" 236 | 237 | // This struct must be in sync with union bpf_attr's anonymous struct used by 238 | // BPF_OBJ_*_ commands 239 | type bpfAttrObjOp struct { 240 | pathname uint64 241 | fd uint32 242 | pad0 [4]byte 243 | } 244 | 245 | // ObjPin stores the map's fd in pathname. 246 | func ObjPin(fd int, pathname string) error { 247 | pathStr := C.CString(pathname) 248 | defer C.free(unsafe.Pointer(pathStr)) 249 | uba := bpfAttrObjOp{ 250 | pathname: uint64(uintptr(unsafe.Pointer(pathStr))), 251 | fd: uint32(fd), 252 | } 253 | 254 | ret, _, err := unix.Syscall( 255 | unix.SYS_BPF, 256 | BPF_OBJ_PIN, 257 | uintptr(unsafe.Pointer(&uba)), 258 | unsafe.Sizeof(uba), 259 | ) 260 | 261 | if ret != 0 || err != 0 { 262 | return errors.New(fmt.Sprintf("Unable to pin object with file descriptor %d to %s: %s", fd, pathname, err)) 263 | } 264 | 265 | return nil 266 | } 267 | 268 | // ObjGet reads the pathname and returns the map's fd read. 269 | func ObjGet(pathname string) (int, error) { 270 | pathStr := C.CString(pathname) 271 | defer C.free(unsafe.Pointer(pathStr)) 272 | uba := bpfAttrObjOp{ 273 | pathname: uint64(uintptr(unsafe.Pointer(pathStr))), 274 | } 275 | 276 | fd, _, err := unix.Syscall( 277 | unix.SYS_BPF, 278 | BPF_OBJ_GET, 279 | uintptr(unsafe.Pointer(&uba)), 280 | unsafe.Sizeof(uba), 281 | ) 282 | 283 | if fd == 0 || err != 0 { 284 | return 0, &os.PathError{ 285 | Op: "Unable to get object", 286 | Err: err, 287 | Path: pathname, 288 | } 289 | } 290 | 291 | return int(fd), nil 292 | } 293 | -------------------------------------------------------------------------------- /pkg/xdptool/possible_cpus.go: -------------------------------------------------------------------------------- 1 | package xdptool 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "os" 7 | 8 | "github.com/pkg/errors" 9 | ) 10 | 11 | const ( 12 | // possibleCPUsFileLength matches the buffer size for CPUs. 13 | // Reference bpf_num_possible_cpus from 14 | // https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf.git/tree/tools/testing/selftests/bpf/bpf_util.h 15 | possibleCPUsFileLength = 128 16 | ) 17 | 18 | var ( 19 | PossibleCpus int 20 | ) 21 | 22 | func PossibleCpuInit() { 23 | calculateNumCpus() 24 | } 25 | 26 | // calculateNumCpus replicates the bpf linux helper equivalent `bpf_num_possible_cpus` 27 | // to find total number of possible CPUs i.e CPUs that have been allocated 28 | // resources and can be brought online if they are present. 29 | // Reference bpf_num_possible_cpus from 30 | // https://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf.git/tree/tools/testing/selftests/bpf/bpf_util.h 31 | // https://github.com/cilium/cilium/blob/master/pkg/maps/metricsmap/metricsmap.go 32 | func calculateNumCpus() { 33 | var start, end int 34 | 35 | file, err := os.Open("/sys/devices/system/cpu/possible") 36 | if err != nil { 37 | panic(errors.Wrap(err, "unable to open sysfs to get CPU count")) 38 | } 39 | defer file.Close() 40 | 41 | data := make([]byte, possibleCPUsFileLength) 42 | for { 43 | _, err := file.Read(data) 44 | if err != nil { 45 | if err == io.EOF { 46 | break 47 | } 48 | panic(errors.Wrap(err, "unable to open sysfs to get CPU count")) 49 | } 50 | n, err := fmt.Sscanf(string(data), "%d-%d", &start, &end) 51 | if err != nil { 52 | panic(errors.Wrap(err, "unable to open sysfs to get CPU count")) 53 | } 54 | if n == 0 { 55 | panic(errors.Wrap(err, "failed to retrieve number of possible CPUs!")) 56 | } else if n == 1 { 57 | end = start 58 | } 59 | if start == 0 { 60 | PossibleCpus = end + 1 61 | } else { 62 | PossibleCpus = 0 63 | } 64 | break 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /scripts/README.md: -------------------------------------------------------------------------------- 1 | # setup 2 | You can develop on sakura cloud. 3 | * required 4 | * terraform(0.12.5 <= x) 5 | * ansible(2.8.3 <= x) 6 | * terraform-provider-sakuracloud(2.2.0 <= x) 7 | * hint: [Terraform for さくらのクラウド](https://sacloud.github.io/terraform-provider-sakuracloud/installation/) 8 | 9 | ## Prepare Create Archive image 10 | ```sh 11 | sudo apt update 12 | sudo apt upgrade -y 13 | sudo apt install -y make 14 | # selected ubuntu image. 15 | git clone https://github.com/usbkey9/uktools && cd uktools 16 | # download version. 17 | make 18 | # select new kernel 19 | sudo uktools-upgrade 20 | 21 | sudo apt update 22 | sudo apt upgrade -y 23 | sudo apt install -y bison flex clang gcc llvm libelf-dev bc libssl-dev tmux trace-cmd pkg-config libtalloc-dev libpcsclite-dev libmnl-dev autoconf libtool binutils-dev libelf-dev libreadline-dev ethtool 24 | 25 | cd ~ 26 | wget https://mirrors.edge.kernel.org/pub/linux/utils/net/iproute2/iproute2-5.5.0.tar.gz 27 | tar -xzvf ./iproute2-5.5.0.tar.gz 28 | cd ./iproute2-5.5.0 29 | sudo make && sudo make install 30 | 31 | cd ~ 32 | sudo ldconfig -v 33 | git clone git://git.osmocom.org/libgtpnl.git 34 | cd libgtpnl 35 | autoreconf -fi 36 | ./configure 37 | make 38 | sudo make install 39 | sudo ldconfig 40 | 41 | # bpftools 42 | cd ~ 43 | version=$(uname -r) 44 | KERNEL_VERSION="v${version:0:3}" 45 | git clone --branch $KERNEL_VERSION --depth 1 https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git 46 | cd linux/tools/bpf 47 | sudo make 48 | sudo cp bpftool/bpftool /usr/local/bin/ 49 | 50 | # golang 51 | 52 | wget https://dl.google.com/go/go1.14.linux-amd64.tar.gz 53 | sudo tar -C /usr/local -xzf go1.14.linux-amd64.tar.gz 54 | echo "export PATH=\$PATH:/usr/local/go/bin" >> /home/toor/.bashrc 55 | echo "export GOPATH=\$HOME/go" >> /home/toor/.bashrc 56 | echo "export PATH=\$PATH:\$GOPATH/bin" >> /home/toor/.bashrc 57 | 58 | export PATH=$PATH:/usr/local/go/bin 59 | ``` 60 | 61 | ## Go Setup 62 | 63 | select archive image(fix terraform file) 64 | 65 | ```sh 66 | terraform init 67 | terraform apply 68 | chmod +x inventry_handler.py 69 | ssh-keygen -f ~/.ssh/toor 70 | ansible-playbook -u ubuntu --private-key=./id_rsa -i inventry_handler.py setup.yml --extra-vars "ansible_sudo_pass=PUT_YOUR_PASSWORD_HERE" 71 | ``` 72 | -------------------------------------------------------------------------------- /scripts/Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | Vagrant.configure("2") do |config| 3 | config.vm.box = "bento/ubuntu-19.04" 4 | 5 | config.vm.provider "virtualbox" do |vb| 6 | vb.memory = "2048" 7 | vb.cpus = 2 8 | end 9 | 10 | config.vm.synced_folder "..", "/home/vagrant/workspace" 11 | config.vm.provision "shell", privileged: true, path: "./env_setup.sh" 12 | end 13 | -------------------------------------------------------------------------------- /scripts/ansible.cfg: -------------------------------------------------------------------------------- 1 | [defaults] 2 | forks = 100 3 | 4 | [ssh_connection] 5 | ssh_args = -o ControlMaster=auto -o ControlPersist=60s -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null 6 | retry_files_enabled=False 7 | callback_whitelist=profile_tasks 8 | -------------------------------------------------------------------------------- /scripts/create-namespaces.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -eu 3 | 4 | if [[ $(id -u) -ne 0 ]] ; then 5 | echo "Please run with sudo" 6 | exit 1 7 | fi 8 | 9 | run () { 10 | echo "$@" 11 | "$@" || exit 1 12 | } 13 | 14 | create_router1 () { 15 | # setup namespaces 16 | run ip netns add host1 17 | run ip netns add router1 18 | 19 | # setup veth peer 20 | run ip link add veth-h1-rt1 type veth peer name veth-rt1-h1 21 | run ip link set veth-h1-rt1 netns host1 22 | run ip link set veth-rt1-h1 netns router1 23 | 24 | # host1 configuraiton 25 | run ip netns exec host1 ip link set lo up 26 | run ip netns exec host1 ip addr add 10.0.1.1/32 dev lo 27 | run ip netns exec host1 ip addr add 172.0.1.1/24 dev veth-h1-rt1 28 | run ip netns exec host1 ip link set veth-h1-rt1 up 29 | run ip netns exec host1 ip route add 10.0.2.0/24 via 172.0.1.2 30 | run ip netns exec host1 ip route add 172.0.2.0/24 via 172.0.1.2 31 | 32 | # router1 configuration 33 | run ip netns exec router1 ip link set lo up 34 | run ip netns exec router1 ip link set veth-rt1-h1 up 35 | run ip netns exec router1 ip -6 addr add fc00:1::1/128 dev lo 36 | run ip netns exec router1 ip addr add 172.0.1.2/24 dev veth-rt1-h1 37 | 38 | # sysctl for router1 39 | ip netns exec router1 sysctl net.ipv4.conf.all.forwarding=1 40 | ip netns exec router1 sysctl net.ipv6.conf.all.forwarding=1 41 | ip netns exec router1 sysctl net.ipv4.conf.all.rp_filter=0 42 | } 43 | 44 | create_router2 () { 45 | # setup namespaces 46 | run ip netns add router2 47 | 48 | # router2 configuration 49 | run ip netns exec router2 ip link set lo up 50 | run ip netns exec router2 ip -6 addr add fc00:2::2/128 dev lo 51 | 52 | # sysctl for router2 53 | ip netns exec router2 sysctl -w net.ipv4.conf.all.forwarding=1 54 | ip netns exec router2 sysctl -w net.ipv6.conf.all.forwarding=1 55 | ip netns exec router2 sysctl -w net.ipv4.conf.all.rp_filter=0 56 | } 57 | 58 | create_router3 () { 59 | # setup namespaces 60 | run ip netns add host2 61 | run ip netns add router3 62 | 63 | # setup veth peer 64 | run ip link add veth-h2-rt3 type veth peer name veth-rt3-h2 65 | run ip link set veth-h2-rt3 netns host2 66 | run ip link set veth-rt3-h2 netns router3 67 | 68 | # host2 configuraiton 69 | run ip netns exec host2 ip link set lo up 70 | run ip netns exec host2 ip link set veth-h2-rt3 up 71 | run ip netns exec host2 ip addr add 10.0.1.2/32 dev lo 72 | run ip netns exec host2 ip addr add 172.0.2.1/24 dev veth-h2-rt3 73 | run ip netns exec host2 ip route add 10.0.1.0/24 via 172.0.2.2 74 | run ip netns exec host2 ip route add 172.0.1.0/24 via 172.0.2.2 75 | 76 | 77 | # router3 configuration 78 | run ip netns exec router3 ip link set lo up 79 | run ip netns exec router3 ip link set veth-rt3-h2 up 80 | run ip netns exec router3 ip -6 addr add fc00:3::3/128 dev lo 81 | run ip netns exec router3 ip addr add 172.0.2.2/24 dev veth-rt3-h2 82 | 83 | # sysctl for router3 84 | ip netns exec router3 sysctl -w net.ipv4.conf.all.forwarding=1 85 | ip netns exec router3 sysctl -w net.ipv6.conf.all.forwarding=1 86 | ip netns exec router3 sysctl -w net.ipv4.conf.all.rp_filter=0 87 | } 88 | 89 | connect_rt1_rt2 () { 90 | # create veth peer 91 | run ip link add veth-rt1-rt2 type veth peer name veth-rt2-rt1 92 | run ip link set veth-rt1-rt2 netns router1 93 | run ip link set veth-rt2-rt1 netns router2 94 | 95 | # configure router1 96 | run ip netns exec router1 ip link set veth-rt1-rt2 up 97 | run ip netns exec router1 ip addr add fc00:12::1/64 dev veth-rt1-rt2 98 | run ip netns exec router1 ip -6 route add fc00:23::/64 via fc00:12::2 99 | run ip netns exec router1 ip -6 route add fc00:2::/64 via fc00:12::2 100 | run ip netns exec router1 ip -6 route add fc00:3::/64 via fc00:12::2 101 | 102 | # configure router2 103 | run ip netns exec router2 ip link set veth-rt2-rt1 up 104 | run ip netns exec router2 ip addr add fc00:12::2/64 dev veth-rt2-rt1 105 | run ip netns exec router2 ip -6 route add fc00:1::/64 via fc00:12::1 106 | run ip netns exec router2 ip -6 route add fc00:12::/64 via fc00:12::1 107 | 108 | } 109 | 110 | connect_rt2_rt3 () { 111 | # create veth peer 112 | run ip link add veth-rt2-rt3 type veth peer name veth-rt3-rt2 113 | run ip link set veth-rt2-rt3 netns router2 114 | run ip link set veth-rt3-rt2 netns router3 115 | 116 | # configure router2 117 | run ip netns exec router2 ip link set veth-rt2-rt3 up 118 | run ip netns exec router2 ip addr add fc00:23::2/64 dev veth-rt2-rt3 119 | run ip netns exec router2 ip -6 route add fc00:3::/64 via fc00:23::1 120 | run ip netns exec router2 ip -6 route add fc00:23::/64 via fc00:23::1 121 | 122 | 123 | # configure router3 124 | run ip netns exec router3 ip link set veth-rt3-rt2 up 125 | run ip netns exec router3 ip addr add fc00:23::1/64 dev veth-rt3-rt2 126 | run ip netns exec router3 ip -6 route add fc00:1::/64 via fc00:23::2 127 | run ip netns exec router3 ip -6 route add fc00:2::/64 via fc00:23::2 128 | run ip netns exec router3 ip -6 route add fc00:12::/64 via fc00:23::2 129 | 130 | } 131 | 132 | destroy_network () { 133 | run ip netns del router1 134 | run ip netns del host1 135 | 136 | run ip netns del router2 137 | 138 | run ip netns del router3 139 | run ip netns del host2 140 | } 141 | 142 | stop () { 143 | destroy_network 144 | } 145 | 146 | router1_srv6(){ 147 | run ip netns exec router1 sysctl -w net.ipv6.conf.all.seg6_enabled=1 148 | run ip netns exec router1 sysctl -w net.ipv6.conf.default.seg6_enabled=1 149 | run ip netns exec router1 sysctl -w net.ipv6.conf.veth-rt1-h1.seg6_enabled=1 150 | run ip netns exec router1 sysctl -w net.ipv6.conf.veth-rt1-rt2.seg6_enabled=1 151 | run ip netns exec router1 sysctl -w net.ipv4.conf.all.rp_filter=0 152 | run ip netns exec router1 sysctl -w net.ipv4.conf.veth-rt1-rt2.rp_filter=0 153 | run ip netns exec router1 sysctl -w net.ipv4.conf.veth-rt1-h1.rp_filter=0 154 | 155 | run ip netns exec router1 ip route add 172.0.2.0/24 encap seg6 mode encap segs fc00:2::1,fc00:3::3 dev veth-rt1-rt2 156 | run ip netns exec router1 ip -6 route del local fc00:1::1 157 | run ip netns exec router1 ip -6 route add local fc00:1::1/128 encap seg6local action End.DX4 nh4 172.0.1.1 dev veth-rt1-h1 158 | } 159 | 160 | router2_srv6(){ 161 | run ip netns exec router2 sysctl -w net.ipv6.conf.all.seg6_enabled=1 162 | run ip netns exec router2 sysctl -w net.ipv6.conf.default.seg6_enabled=1 163 | run ip netns exec router2 sysctl -w net.ipv6.conf.veth-rt2-rt1.seg6_enabled=1 164 | run ip netns exec router2 sysctl -w net.ipv6.conf.veth-rt2-rt3.seg6_enabled=1 165 | 166 | # run ip netns exec router2 ip -6 route add fc00:2::1/128 encap seg6local action End dev veth-rt2-rt1 167 | # run ip netns exec router2 ip -6 route add fc00:2::2/128 encap seg6local action End dev veth-rt2-rt3 168 | } 169 | 170 | router3_srv6(){ 171 | run ip netns exec router3 sysctl -w net.ipv6.conf.all.seg6_enabled=1 172 | run ip netns exec router3 sysctl -w net.ipv6.conf.default.seg6_enabled=1 173 | run ip netns exec router3 sysctl -w net.ipv6.conf.veth-rt3-h2.seg6_enabled=1 174 | run ip netns exec router3 sysctl -w net.ipv6.conf.veth-rt3-rt2.seg6_enabled=1 175 | run ip netns exec router3 sysctl -w net.ipv4.conf.all.rp_filter=0 176 | run ip netns exec router3 sysctl -w net.ipv4.conf.veth-rt3-rt2.rp_filter=0 177 | run ip netns exec router3 sysctl -w net.ipv4.conf.veth-rt3-h2.rp_filter=0 178 | 179 | run ip netns exec router3 ip route add 172.0.1.0/24 encap seg6 mode encap segs fc00:2::2,fc00:1::1 dev veth-rt3-rt2 180 | run ip netns exec router3 ip -6 route del local fc00:3::3 181 | run ip netns exec router3 ip -6 route add local fc00:3::3/128 encap seg6local action End.DX4 nh4 172.0.2.1 dev veth-rt3-h2 182 | } 183 | 184 | trap stop 0 1 2 3 13 14 15 185 | 186 | # exec functions 187 | create_router1 188 | create_router2 189 | create_router3 190 | 191 | connect_rt1_rt2 192 | connect_rt2_rt3 193 | 194 | router1_srv6 195 | router2_srv6 196 | router3_srv6 197 | 198 | status=0; $SHELL || status=$? 199 | exit $status 200 | -------------------------------------------------------------------------------- /scripts/dev_setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | version=$(uname -r) 4 | KERNEL_VERSION="v${version:0:3}" 5 | 6 | root_dir=$(pwd) 7 | echo $KERNEL_VERSION 8 | rm -rf build 9 | mkdir build 10 | 11 | git clone --branch $KERNEL_VERSION --depth 1 https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git ./build/linux 12 | pushd ./build/linux 13 | make defconfig 14 | make headers_install 15 | popd 16 | mkdir out 17 | -------------------------------------------------------------------------------- /scripts/env_setup.sh: -------------------------------------------------------------------------------- 1 | #/bin/sh 2 | 3 | # install dependencies for building iproute2 4 | apt update 5 | DEBIAN_FRONTEND=noninteractive apt upgrade -y 6 | apt install -y bison flex clang gcc llvm libelf-dev bc libssl-dev tmux trace-cmd linux-headers-`uname -r` 7 | 8 | # update iproute2 9 | sudo apt install -y pkg-config bison flex make gcc 10 | cd /tmp 11 | wget https://mirrors.edge.kernel.org/pub/linux/utils/net/iproute2/iproute2-5.5.0.tar.gz 12 | tar -xzvf ./iproute2-5.5.0.tar.gz 13 | cd ./iproute2-5.5.0 14 | 15 | sudo make && sudo make install 16 | 17 | # enable gtp and install 18 | cd /home/vagrant 19 | sudo apt -y install libtalloc-dev libpcsclite-dev libmnl-dev autoconf libtool 20 | sudo ldconfig -v 21 | git clone git://git.osmocom.org/libgtpnl.git 22 | cd libgtpnl 23 | autoreconf -fi 24 | ./configure 25 | make 26 | sudo make install 27 | sudo ldconfig 28 | -------------------------------------------------------------------------------- /scripts/inventry_handler.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:utf-8 -*- 3 | 4 | import os 5 | import subprocess 6 | import json 7 | 8 | __location__ = os.path.realpath( 9 | os.path.join(os.getcwd(), os.path.dirname(__file__))) 10 | 11 | filepath = "terraform.tfstate" 12 | 13 | with open(os.path.join(__location__, filepath)) as f: 14 | hosts = json.loads(f.read()) 15 | 16 | inventory = { 17 | "cloud_servers":{"hosts":[]}, 18 | "_meta": {"hostvars": {}} 19 | } 20 | 21 | for host_name in hosts["outputs"]: 22 | ipaddrs = hosts["outputs"][host_name]['value'] 23 | inventory["cloud_servers"]["hosts"].append(host_name) 24 | inventory["_meta"]["hostvars"][host_name] = {"ansible_host": ipaddrs} 25 | 26 | print(json.dumps(inventory)) 27 | -------------------------------------------------------------------------------- /scripts/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 0.12.5" 3 | required_providers { 4 | sakuracloud = { 5 | source = "sacloud/sakuracloud" 6 | 7 | # We recommend pinning to the specific version of the SakuraCloud Provider you're using 8 | # since new versions are released frequently 9 | version = "~> 2" 10 | } 11 | } 12 | } 13 | # Configure the SakuraCloud Provider 14 | provider "sakuracloud" { 15 | } 16 | 17 | # ubuntu archive 18 | data sakuracloud_archive "ubuntu-archive" { 19 | filter { 20 | tags = ["ubuntu", "2004", "5.8"] 21 | } 22 | } 23 | 24 | # pub key 25 | resource sakuracloud_ssh_key_gen "key" { 26 | name = "pubkey" 27 | 28 | provisioner "local-exec" { 29 | command = "echo \"${self.private_key}\" > id_rsa; chmod 0600 id_rsa" 30 | } 31 | 32 | provisioner "local-exec" { 33 | when = destroy 34 | command = "rm -f id_rsa" 35 | } 36 | } 37 | 38 | # switch 39 | resource sakuracloud_switch "h1-rt1-switch" { 40 | name = "h1-rt1-switch" 41 | } 42 | resource sakuracloud_switch "rt1-rt2-switch" { 43 | name = "rt1-rt2-switch" 44 | } 45 | resource sakuracloud_switch "rt2-rt3-switch" { 46 | name = "rt2-rt3-switch" 47 | } 48 | resource sakuracloud_switch "rt3-h2-switch" { 49 | name = "rt3-h2-switch" 50 | } 51 | 52 | # disks 53 | resource sakuracloud_disk "host-01-disk" { 54 | name = "host-01" 55 | source_archive_id = data.sakuracloud_archive.ubuntu-archive.id 56 | size = 100 57 | tags = ["srv6"] 58 | } 59 | resource sakuracloud_disk "host-02-disk" { 60 | name = "host-02" 61 | source_archive_id = data.sakuracloud_archive.ubuntu-archive.id 62 | size = 100 63 | tags = ["srv6"] 64 | } 65 | resource sakuracloud_disk "router-01-disk" { 66 | name = "router-01" 67 | source_archive_id = data.sakuracloud_archive.ubuntu-archive.id 68 | size = 100 69 | tags = ["srv6"] 70 | } 71 | resource sakuracloud_disk "router-02-disk" { 72 | name = "router-02" 73 | source_archive_id = data.sakuracloud_archive.ubuntu-archive.id 74 | size = 100 75 | tags = ["srv6"] 76 | } 77 | resource sakuracloud_disk "router-03-disk" { 78 | name = "router-03" 79 | source_archive_id = data.sakuracloud_archive.ubuntu-archive.id 80 | size = 100 81 | tags = ["srv6"] 82 | } 83 | 84 | # servers 85 | resource sakuracloud_server "host-01-server" { 86 | name = "host-01" 87 | core = 4 88 | memory = 4 89 | disks = [sakuracloud_disk.host-01-disk.id] 90 | tags = ["@nic-double-queue", "srv6"] 91 | 92 | network_interface { 93 | upstream = "shared" 94 | } 95 | network_interface { 96 | upstream = sakuracloud_switch.h1-rt1-switch.id 97 | } 98 | disk_edit_parameter { 99 | hostname = "host-01" 100 | ssh_key_ids = [sakuracloud_ssh_key_gen.key.id] 101 | password = "PUT_YOUR_PASSWORD_HERE" 102 | disable_pw_auth = "true" 103 | } 104 | } 105 | 106 | resource sakuracloud_server "router-01-server" { 107 | name = "router-01" 108 | core = 4 109 | memory = 4 110 | disks = [sakuracloud_disk.router-01-disk.id] 111 | tags = ["@nic-double-queue", "srv6"] 112 | network_interface { 113 | upstream = "shared" 114 | } 115 | network_interface { 116 | upstream = sakuracloud_switch.h1-rt1-switch.id 117 | } 118 | network_interface { 119 | upstream = sakuracloud_switch.rt1-rt2-switch.id 120 | } 121 | disk_edit_parameter { 122 | hostname = "router-01" 123 | ssh_key_ids = [sakuracloud_ssh_key_gen.key.id] 124 | password = "PUT_YOUR_PASSWORD_HERE" 125 | disable_pw_auth = "true" 126 | } 127 | } 128 | 129 | resource sakuracloud_server "router-02-server" { 130 | name = "router-02" 131 | core = 4 132 | memory = 4 133 | disks = [sakuracloud_disk.router-02-disk.id] 134 | tags = ["@nic-double-queue", "srv6"] 135 | network_interface { 136 | upstream = "shared" 137 | } 138 | network_interface { 139 | upstream = sakuracloud_switch.rt1-rt2-switch.id 140 | } 141 | network_interface { 142 | upstream = sakuracloud_switch.rt2-rt3-switch.id 143 | } 144 | disk_edit_parameter { 145 | hostname = "router-02" 146 | ssh_key_ids = [sakuracloud_ssh_key_gen.key.id] 147 | password = "PUT_YOUR_PASSWORD_HERE" 148 | disable_pw_auth = "true" 149 | } 150 | } 151 | 152 | resource sakuracloud_server "router-03-server" { 153 | name = "router-03" 154 | core = 4 155 | memory = 4 156 | disks = [sakuracloud_disk.router-03-disk.id] 157 | tags = ["@nic-double-queue", "srv6"] 158 | network_interface { 159 | upstream = "shared" 160 | } 161 | network_interface { 162 | upstream = sakuracloud_switch.rt2-rt3-switch.id 163 | } 164 | network_interface { 165 | upstream = sakuracloud_switch.rt3-h2-switch.id 166 | } 167 | disk_edit_parameter { 168 | hostname = "router-03" 169 | ssh_key_ids = [sakuracloud_ssh_key_gen.key.id] 170 | password = "PUT_YOUR_PASSWORD_HERE" 171 | disable_pw_auth = "true" 172 | } 173 | } 174 | resource sakuracloud_server "host-02-server" { 175 | name = "host-02" 176 | core = 4 177 | memory = 4 178 | disks = [sakuracloud_disk.host-02-disk.id] 179 | tags = ["@nic-double-queue", "srv6"] 180 | 181 | network_interface { 182 | upstream = "shared" 183 | } 184 | network_interface { 185 | upstream = sakuracloud_switch.rt3-h2-switch.id 186 | } 187 | disk_edit_parameter { 188 | hostname = "host-02" 189 | ssh_key_ids = [sakuracloud_ssh_key_gen.key.id] 190 | password = "PUT_YOUR_PASSWORD_HERE" 191 | disable_pw_auth = "true" 192 | } 193 | } 194 | 195 | output "host-01" { 196 | value = "${sakuracloud_server.host-01-server.ip_address}" 197 | } 198 | 199 | output "router-01" { 200 | value = "${sakuracloud_server.router-01-server.ip_address}" 201 | } 202 | 203 | output "router-02" { 204 | value = "${sakuracloud_server.router-02-server.ip_address}" 205 | } 206 | 207 | output "router-03" { 208 | value = "${sakuracloud_server.router-03-server.ip_address}" 209 | } 210 | 211 | output "host-02" { 212 | value = "${sakuracloud_server.host-02-server.ip_address}" 213 | } 214 | -------------------------------------------------------------------------------- /scripts/setup.yml: -------------------------------------------------------------------------------- 1 | # common setup phase 2 | - hosts: cloud_servers 3 | become: yes 4 | vars_files: 5 | - var.yml 6 | tasks: 7 | - name: add a users group 8 | group: name={{ item.name }} state=present 9 | with_items: 10 | - "{{ users }}" 11 | - name: add a new user 12 | user: 13 | name: "{{ item.name }}" 14 | uid: "{{ item.uid}}" 15 | password: "{{ item.password | password_hash('sha512') }}" 16 | group: "{{ item.name }}" 17 | shell: /bin/bash 18 | groups: sudo 19 | state: present 20 | with_items: 21 | - "{{ users }}" 22 | - name: mkdir .ssh 23 | file: 24 | path: /home/{{ item.name }}/.ssh/ 25 | state: directory 26 | owner: "{{ item.name }}" 27 | group: sudo 28 | mode: 0700 29 | with_items: 30 | - "{{ users }}" 31 | - name: add authorized_key 32 | authorized_key: 33 | user: "{{ item.name }}" 34 | key: "{{ lookup('file', '~/.ssh/toor.pub') }}" 35 | with_items: 36 | - "{{ users }}" 37 | - name: add to sudoers 38 | lineinfile: 39 | dest: /etc/sudoers 40 | line: "{{ item.name }} ALL=(ALL) NOPASSWD:ALL" 41 | with_items: 42 | - "{{ users }}" 43 | 44 | - name: routing file 45 | copy: 46 | src: ./setup_systemd/{{ inventory_hostname }}.sh 47 | dest: /opt/netsetup.sh 48 | owner: root 49 | group: root 50 | mode: 755 51 | - name: routing systemd 52 | copy: 53 | src: ./setup_systemd/netsetup.service 54 | dest: /etc/systemd/system/netsetup.service 55 | owner: root 56 | group: root 57 | - name: netsetup.service restart 58 | service: 59 | name: netsetup.service 60 | daemon_reload: yes 61 | state: restarted 62 | enabled: yes 63 | -------------------------------------------------------------------------------- /scripts/setup_systemd/host-01.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # addr configuraiton 4 | sudo ip link set lo up 5 | sudo ip addr add 10.0.1.1/32 dev lo 6 | sudo ip link set eth1 up 7 | sudo ip addr add 172.0.1.1/24 dev eth1 8 | sudo ip route add 10.0.2.0/24 via 172.0.1.2 9 | sudo ip route add 172.0.2.0/24 via 172.0.1.2 10 | 11 | ethtool -L eth0 combined 2 12 | ethtool -L eth1 combined 2 13 | -------------------------------------------------------------------------------- /scripts/setup_systemd/host-02.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # addr configuraiton 4 | sudo ip link set lo up 5 | sudo ip addr add 10.0.1.2/32 dev lo 6 | sudo ip link set eth1 up 7 | sudo ip addr add 172.0.2.1/24 dev eth1 8 | sudo ip route add 10.0.1.0/24 via 172.0.2.2 9 | sudo ip route add 172.0.1.0/24 via 172.0.2.2 10 | 11 | ethtool -L eth0 combined 2 12 | ethtool -L eth1 combined 2 13 | -------------------------------------------------------------------------------- /scripts/setup_systemd/netsetup.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description = iproute2 auto start daemon 3 | 4 | [Service] 5 | ExecStart = /opt/netsetup.sh 6 | Type=oneshot 7 | 8 | [Install] 9 | WantedBy = multi-user.target 10 | -------------------------------------------------------------------------------- /scripts/setup_systemd/router-01.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # addr configuraiton 3 | sudo ip link set lo up 4 | sudo ip addr add fc00:1::1/128 dev lo 5 | sudo ip link set eth1 up 6 | sudo ip addr add 172.0.1.2/24 dev eth1 7 | sudo ip link set eth2 up 8 | sudo ip addr add fc00:12::1/64 dev eth2 9 | 10 | sudo ip -6 route add fc00:23::/48 via fc00:12::2 11 | sudo ip -6 route add fc00:2::/48 via fc00:12::2 12 | sudo ip -6 route add fc00:3::/48 via fc00:12::2 13 | 14 | # seg6 15 | sudo sysctl net.ipv4.conf.all.forwarding=1 16 | sudo sysctl net.ipv6.conf.all.forwarding=1 17 | sudo sysctl net.ipv4.conf.all.rp_filter=0 18 | sudo sysctl net.ipv4.conf.eth1.rp_filter=0 19 | 20 | sudo sysctl net.ipv6.conf.all.seg6_enabled=1 21 | sudo sysctl net.ipv6.conf.default.seg6_enabled=1 22 | sudo sysctl net.ipv6.conf.eth1.seg6_enabled=1 23 | sudo sysctl net.ipv6.conf.eth2.seg6_enabled=1 24 | 25 | # sudo ip route add 172.0.2.0/24 encap seg6 mode encap segs fc00:2::1,fc00:3::3 dev eth2 26 | # sudo ip -6 route del local fc00:1::1 27 | # sudo ip -6 route add local fc00:1::1/128 encap seg6local action End.DX4 nh4 172.0.1.1 dev eth2 28 | 29 | # ↑same config 30 | # internal: 31 | # # logfile: "/var/log/vinbero.log" 32 | # logfile: "./vinbero.log" 33 | # development: false 34 | # devices: 35 | # - eth1 36 | # - eth2 37 | # settings: 38 | # functions: 39 | # - action: SEG6_LOCAL_ACTION_END_DX4 40 | # triggerAddr: fc00:1::1/128 41 | # nexthop: 172.0.1.1 42 | # transitv4: 43 | # - action: SEG6_IPTUN_MODE_ENCAP 44 | # triggerAddr: 172.0.2.0/24 45 | # actionSrcAddr: fc00:1::1 46 | # segments: 47 | # - fc00:3::3 # last arrive next hop 48 | # - fc00:2::1 49 | 50 | ethtool -L eth0 combined 2 51 | ethtool -L eth1 combined 2 52 | ethtool -L eth2 combined 2 53 | -------------------------------------------------------------------------------- /scripts/setup_systemd/router-02.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # addr configuraiton 4 | sudo ip link set lo up 5 | sudo ip addr add fc00:2::2/128 dev lo 6 | sudo ip link set eth1 up 7 | sudo ip addr add fc00:12::2/64 dev eth1 8 | sudo ip link set eth2 up 9 | sudo ip addr add fc00:23::2/64 dev eth2 10 | 11 | # to route1 12 | sudo ip -6 route add fc00:1::/48 via fc00:12::1 13 | sudo ip -6 route add fc00:12::/48 via fc00:12::1 14 | sudo ip -6 route add fc00:3::/48 via fc00:23::1 15 | sudo ip -6 route add fc00:23::/48 via fc00:23::1 16 | 17 | # seg6 18 | sudo sysctl -w net.ipv4.conf.all.forwarding=1 19 | sudo sysctl -w net.ipv6.conf.all.forwarding=1 20 | sudo sysctl -w net.ipv4.conf.all.rp_filter=0 21 | 22 | sudo sysctl -w net.ipv6.conf.all.seg6_enabled=1 23 | sudo sysctl -w net.ipv6.conf.eth1.seg6_enabled=1 24 | sudo sysctl -w net.ipv6.conf.eth2.seg6_enabled=1 25 | 26 | # sudo ip -6 route add fc00:2::1/128 encap seg6local action End dev eth1 27 | # sudo ip -6 route add fc00:2::2/128 encap seg6local action End dev eth2 28 | 29 | # ↑same config 30 | # internal: 31 | # logfile: "./vinbero.log" 32 | # development: false 33 | # devices: 34 | # - eth1 35 | # - eth2 36 | # settings: 37 | # functions: 38 | # - action: SEG6_LOCAL_ACTION_END 39 | # triggerAddr: fc00:2::1/128 40 | # actionSrcAddr: fc00:1::1 41 | # - action: SEG6_LOCAL_ACTION_END 42 | # triggerAddr: fc00:2::2/128 43 | # actionSrcAddr: fc00:1::1 44 | 45 | ethtool -L eth0 combined 2 46 | ethtool -L eth1 combined 2 47 | ethtool -L eth2 combined 2 48 | -------------------------------------------------------------------------------- /scripts/setup_systemd/router-03.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # addr configuraiton 4 | sudo ip link set lo up 5 | sudo ip addr add fc00:3::3/128 dev lo 6 | sudo ip link set eth1 up 7 | sudo ip addr add fc00:23::1/64 dev eth1 8 | sudo ip link set eth2 up 9 | sudo ip addr add 172.0.2.2/24 dev eth2 10 | 11 | ip -6 route add fc00:1::/64 via fc00:23::2 12 | ip -6 route add fc00:2::/64 via fc00:23::2 13 | ip -6 route add fc00:12::/64 via fc00:23::2 14 | 15 | # seg6 16 | sudo sysctl net.ipv4.conf.all.forwarding=1 17 | sudo sysctl net.ipv6.conf.all.forwarding=1 18 | sudo sysctl net.ipv4.conf.all.rp_filter=0 19 | sudo sysctl net.ipv4.conf.eth1.rp_filter=0 20 | sudo sysctl net.ipv4.conf.eth2.rp_filter=0 21 | 22 | sudo sysctl net.ipv6.conf.all.seg6_enabled=1 23 | sudo sysctl net.ipv6.conf.default.seg6_enabled=1 24 | sudo sysctl net.ipv6.conf.eth1.seg6_enabled=1 25 | sudo sysctl net.ipv6.conf.eth2.seg6_enabled=1 26 | 27 | # sudo ip route add 172.0.1.0/24 encap seg6 mode encap segs fc00:2::2,fc00:1::1 dev eth2 28 | # sudo ip -6 route del local fc00:3::3 29 | # sudo ip -6 route add local fc00:3::3/128 encap seg6local action End.DX4 nh4 172.0.2.1 dev eth1 30 | 31 | # ↑same config 32 | # internal: 33 | # logfile: "./vinbero.log" 34 | # development: false 35 | # devices: 36 | # - eth1 37 | # - eth2 38 | # settings: 39 | # functions: 40 | # - action: SEG6_LOCAL_ACTION_END_DX4 41 | # triggerAddr: fc00:3::3/128 42 | # nexthop: 172.0.2.1 43 | # transitv4: 44 | # - action: SEG6_IPTUN_MODE_ENCAP 45 | # triggerAddr: 172.0.1.0/24 46 | # actionSrcAddr: fc00:3::3 47 | # segments: 48 | # - fc00:1::1 # last arrive next hop 49 | # - fc00:2::1 50 | 51 | ethtool -L eth0 combined 2 52 | ethtool -L eth1 combined 2 53 | ethtool -L eth2 combined 2 54 | -------------------------------------------------------------------------------- /scripts/var.sample.yml: -------------------------------------------------------------------------------- 1 | users: 2 | - { name: 'hoge', uid: '1011', password: 'fuga' } 3 | -------------------------------------------------------------------------------- /src/srv6.c: -------------------------------------------------------------------------------- 1 | #define KBUILD_MODNAME "xdp_srv6_functions" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include "bpf_helpers.h" 16 | #include "bpf_endian.h" 17 | 18 | #include "srv6_consts.h" 19 | #include "srv6_structs.h" 20 | #include "srv6_maps.h" 21 | #include "srv6_helpers.h" 22 | // #include "srv6_transit.h" 23 | 24 | /* regular endpoint function */ 25 | __attribute__((__always_inline__)) static inline int action_end(struct xdp_md *xdp) 26 | { 27 | void *data = (void *)(long)xdp->data; 28 | void *data_end = (void *)(long)xdp->data_end; 29 | 30 | struct srhhdr *srhdr = get_and_validate_srh(xdp); 31 | struct ipv6hdr *v6h = get_ipv6(xdp); 32 | 33 | if (!srhdr || !v6h) 34 | return XDP_PASS; 35 | 36 | if (!advance_nextseg(srhdr, &v6h->daddr, xdp)) 37 | return XDP_PASS; 38 | 39 | return rewrite_nexthop(xdp, 0); 40 | } 41 | 42 | __attribute__((__always_inline__)) static inline int base_decap(struct xdp_md *xdp, __u16 proto) 43 | { 44 | void *data_end = (void *)(unsigned long)xdp->data_end; 45 | void *data = (void *)(unsigned long)xdp->data; 46 | 47 | struct srhhdr *srh = get_srh(xdp); 48 | struct ipv6hdr *v6h = get_ipv6(xdp); 49 | 50 | if (!srh || !v6h) 51 | { 52 | return XDP_PASS; 53 | } 54 | 55 | if (bpf_xdp_adjust_head(xdp, (int)(sizeof(struct ipv6hdr) + (srh->hdrExtLen + 1) * 8))) 56 | { 57 | return XDP_PASS; 58 | } 59 | 60 | data = (void *)(unsigned long)xdp->data; 61 | data_end = (void *)(unsigned long)xdp->data_end; 62 | struct ethhdr *new_eth = data; 63 | if (new_eth + 1 > data_end) 64 | return XDP_DROP; 65 | 66 | new_eth->h_proto = bpf_htons(proto); 67 | 68 | return NextFIBCheck; 69 | } 70 | 71 | __attribute__((__always_inline__)) static inline int action_enddx4(struct xdp_md *xdp, struct end_function *ef) 72 | { 73 | int rc = base_decap(xdp, ETH_P_IPV4); 74 | if (rc != NextFIBCheck) 75 | { 76 | return rc; 77 | } 78 | 79 | void *data_end = (void *)(unsigned long)xdp->data_end; 80 | void *data = (void *)(unsigned long)xdp->data; 81 | struct iphdr *iph = get_ipv4(xdp); 82 | 83 | if (!iph) 84 | { 85 | return XDP_PASS; 86 | } 87 | 88 | iph->daddr = ef->nexthop.v4.addr; 89 | csum_build(iph); 90 | return rewrite_nexthop(xdp, 0); 91 | } 92 | 93 | __attribute__((__always_inline__)) static inline int base_encap(struct xdp_md *xdp, struct transit_behavior *tb, __u8 nexthdr, __u16 innerlen) 94 | { 95 | void *data = (void *)(long)xdp->data; 96 | void *data_end = (void *)(long)xdp->data_end; 97 | 98 | struct ipv6hdr *v6h; 99 | struct srhhdr *srh; 100 | __u8 srh_len; 101 | 102 | srh_len = sizeof(struct srhhdr) + sizeof(struct in6_addr) * tb->segment_length; 103 | if (bpf_xdp_adjust_head(xdp, 0 - (int)(sizeof(struct ipv6hdr) + srh_len))) 104 | return XDP_PASS; 105 | 106 | data = (void *)(long)xdp->data; 107 | data_end = (void *)(long)xdp->data_end; 108 | 109 | struct ethhdr *new_eth; 110 | new_eth = (void *)data; 111 | 112 | if ((void *)((long)new_eth + sizeof(struct ethhdr)) > data_end) 113 | return XDP_PASS; 114 | 115 | new_eth->h_proto = bpf_htons(ETH_P_IPV6); 116 | 117 | v6h = (void *)data + sizeof(struct ethhdr); 118 | if ((void *)(data + sizeof(struct ethhdr) + sizeof(struct ipv6hdr)) > data_end) 119 | return XDP_PASS; 120 | 121 | v6h->version = 6; 122 | v6h->priority = 0; 123 | v6h->nexthdr = NEXTHDR_ROUTING; 124 | v6h->hop_limit = 64; 125 | v6h->payload_len = bpf_htons(srh_len + innerlen); 126 | __builtin_memcpy(&v6h->saddr, &tb->saddr, sizeof(struct in6_addr)); 127 | 128 | if (tb->segment_length == 0 || tb->segment_length > MAX_SEGMENTS) 129 | return XDP_PASS; 130 | 131 | __builtin_memcpy(&v6h->daddr, &tb->segments[tb->segment_length - 1], sizeof(struct in6_addr)); 132 | 133 | srh = (void *)v6h + sizeof(struct ipv6hdr); 134 | if ((void *)(data + sizeof(struct ethhdr) + sizeof(struct ipv6hdr) + sizeof(struct srhhdr)) > data_end) 135 | return XDP_PASS; 136 | 137 | srh->nextHdr = nexthdr; 138 | srh->hdrExtLen = ((srh_len / 8) - 1); 139 | srh->routingType = 4; 140 | srh->segmentsLeft = tb->segment_length - 1; 141 | srh->lastEntry = tb->segment_length - 1; 142 | srh->flags = 0; 143 | srh->tag = 0; 144 | 145 | #pragma clang loop unroll(full) 146 | for (__u32 i = 0; i < MAX_SEGMENTS; i++) 147 | { 148 | if (i >= tb->segment_length) 149 | break; 150 | 151 | if ((void *)(data + sizeof(struct ethhdr) + sizeof(struct ipv6hdr) + sizeof(struct srhhdr) + sizeof(struct in6_addr) * (i + 1) + 1) > data_end) 152 | return XDP_PASS; 153 | 154 | __builtin_memcpy(&srh->segments[i], &tb->segments[i], sizeof(struct in6_addr)); 155 | } 156 | 157 | return NextFIBCheck; 158 | } 159 | 160 | __attribute__((__always_inline__)) static inline int transit_encap(struct xdp_md *xdp, struct transit_behavior *tb, __u8 nexthdr, __u8 innerlen) 161 | { 162 | int rc = base_encap(xdp, tb, nexthdr, innerlen); 163 | if (rc == NextFIBCheck) 164 | return rewrite_nexthop(xdp, BPF_FIB_LOOKUP_OUTPUT); 165 | return rc; 166 | } 167 | 168 | __attribute__((__always_inline__)) static inline int transit_gtp4_encap(struct xdp_md *xdp, struct transit_behavior *tb) 169 | { 170 | // chack UDP/GTP packet 171 | void *data = (void *)(unsigned long)xdp->data; 172 | void *data_end = (void *)(unsigned long)xdp->data_end; 173 | struct iphdr *iph = get_ipv4(xdp); 174 | __u8 type; 175 | __u32 tid; 176 | __u16 seqNum; 177 | struct ipv6hdr *v6h; 178 | struct srhhdr *srh; 179 | __u8 srh_len; 180 | __u16 decap_len, encap_len; 181 | __u16 innerlen; 182 | __u32 outer_saddr, outer_daddr; 183 | if (!iph) 184 | return XDP_PASS; 185 | 186 | // Check protocol 187 | if (iph->protocol != IPPROTO_UDP) 188 | return XDP_PASS; 189 | 190 | struct gtp1hdr *gtp1h = data + sizeof(struct ethhdr) + sizeof(struct iphdr) + sizeof(struct udphdr); 191 | if (data + sizeof(struct ethhdr) + sizeof(struct iphdr) + sizeof(struct udphdr) + sizeof(struct gtp1hdr) > data_end) 192 | return XDP_PASS; 193 | 194 | __builtin_memmove(&outer_saddr, &iph->saddr, sizeof(__u32)); 195 | __builtin_memmove(&outer_daddr, &iph->daddr, sizeof(__u32)); 196 | 197 | // TODO:: extention header support 198 | decap_len = sizeof(struct iphdr) + sizeof(struct udphdr) + sizeof(struct gtp1hdr); 199 | 200 | tid = gtp1h->tid; 201 | type = gtp1h->type; 202 | innerlen = bpf_ntohs(gtp1h->length); 203 | // GTPV1_GPDU only logic 204 | if (type != GTPV1_GPDU) 205 | return XDP_DROP; 206 | 207 | if (tb->s_prefixlen == 0 || tb->d_prefixlen == 0 || 208 | MAX_GTP4_SRCADDR_PREFIX < tb->s_prefixlen || 209 | MAX_GTP4_DSTADDR_PREFIX < tb->d_prefixlen) 210 | return XDP_DROP; 211 | 212 | // if (type == ECHO_REQUEST || type == ECHO_RESPONSE){ 213 | // seqNum = data + sizeof(struct ethhdr) + sizeof(struct iphdr) + sizeof(struct udphdr) + sizeof(struct gtp1hdr); 214 | 215 | // if (data + sizeof(struct ethhdr) + sizeof(struct iphdr) + sizeof(struct udphdr) + sizeof(struct gtp1hdr) + sizeof(u16) > data_end){ 216 | // return XDP_PASS; 217 | // } 218 | // } 219 | __u32 seg_len = tb->segment_length + 1; 220 | srh_len = sizeof(struct srhhdr) + (sizeof(struct in6_addr) * seg_len); // Add the size of the converted address 221 | 222 | encap_len = sizeof(struct ipv6hdr) + srh_len; 223 | seg_len = seg_len & 0xffff; 224 | // Shrink 225 | if (bpf_xdp_adjust_head(xdp, (int)(decap_len))) 226 | return XDP_PASS; 227 | 228 | if (bpf_xdp_adjust_head(xdp, 0 - (int)(encap_len))) 229 | return XDP_PASS; 230 | 231 | data = (void *)(long)xdp->data; 232 | data_end = (void *)(long)xdp->data_end; 233 | 234 | struct ethhdr *new_eth = (void *)data; 235 | if ((void *)((long)new_eth + sizeof(struct ethhdr)) > data_end) 236 | return XDP_DROP; 237 | 238 | new_eth->h_proto = bpf_htons(ETH_P_IPV6); 239 | 240 | v6h = (void *)data + sizeof(struct ethhdr); 241 | if ((void *)(data + sizeof(struct ethhdr) + sizeof(struct ipv6hdr)) > data_end) 242 | return XDP_DROP; 243 | 244 | v6h->version = 6; 245 | v6h->priority = 0; 246 | v6h->nexthdr = NEXTHDR_ROUTING; 247 | v6h->hop_limit = 64; 248 | v6h->payload_len = bpf_htons(srh_len + innerlen); 249 | __builtin_memcpy(&v6h->saddr, &tb->saddr, sizeof(struct in6_addr)); 250 | if (seg_len < 1 || MAX_SEGMENTS < seg_len) 251 | return XDP_DROP; 252 | 253 | __builtin_memcpy(&v6h->daddr, &tb->segments[seg_len - 1], sizeof(struct in6_addr)); 254 | 255 | struct args_mob_session args; 256 | __u16 s_offset, s_shift, d_offset, d_shift; 257 | 258 | s_offset = tb->s_prefixlen / 8; 259 | s_shift = tb->s_prefixlen % 8; 260 | d_offset = tb->d_prefixlen / 8; 261 | d_shift = tb->d_prefixlen % 8; 262 | 263 | args.qfi = 0; //TODO::GTP-U extension header get QFI 264 | args.r = 0; 265 | args.u = 0; 266 | // args.qfi_r_u = 0; 267 | args.session.pdu_session_id = tid; 268 | 269 | // shift ipv4 addr append 270 | __u8 *src_p, *dst_p, *args_p; 271 | src_p = (__u8 *)&outer_saddr; 272 | dst_p = (__u8 *)&outer_daddr; 273 | args_p = (__u8 *)&args; 274 | 275 | write_v6addr_in_pyload(&v6h->saddr, src_p, sizeof(__u32), s_offset, s_shift, data_end); 276 | 277 | srh = (void *)v6h + sizeof(struct ipv6hdr); 278 | if ((void *)(data + sizeof(struct ethhdr) + sizeof(struct ipv6hdr) + sizeof(struct srhhdr)) > data_end) 279 | return XDP_PASS; 280 | 281 | srh->nextHdr = IPPROTO_IPIP; 282 | srh->hdrExtLen = ((srh_len / 8) - 1); 283 | srh->routingType = 4; 284 | srh->segmentsLeft = seg_len - 1; 285 | srh->lastEntry = seg_len - 1; 286 | srh->flags = 0; 287 | srh->tag = 0; 288 | if ((void *)(data + sizeof(struct ethhdr) + sizeof(struct ipv6hdr) + sizeof(struct srhhdr) + sizeof(struct in6_addr) + 1) > data_end) 289 | return XDP_PASS; 290 | 291 | __builtin_memcpy(&v6h->daddr, &tb->segments[0], sizeof(struct in6_addr)); 292 | __builtin_memcpy(&srh->segments[0], &tb->daddr, sizeof(struct in6_addr)); 293 | write_v6addr_in_pyload(&srh->segments[0], dst_p, sizeof(__u32), d_offset, d_shift, data_end); 294 | d_offset += sizeof(__u32); 295 | write_v6addr_in_pyload(&srh->segments[0], args_p, sizeof(struct args_mob_session), d_offset, d_shift, data_end); 296 | 297 | #pragma clang loop unroll(disable) 298 | for (__u16 i = 1; i < MAX_SEGMENTS; i++) 299 | { 300 | i = i & 0xffff; 301 | if (i >= seg_len) 302 | break; 303 | 304 | if ((void *)(data + sizeof(struct ethhdr) + sizeof(struct ipv6hdr) + sizeof(struct srhhdr) + sizeof(struct in6_addr) * (i + 2) + 1) > data_end) 305 | return XDP_PASS; 306 | 307 | __builtin_memcpy(&srh->segments[i], &tb->segments[i - 1], sizeof(struct in6_addr)); 308 | } 309 | 310 | return rewrite_nexthop(xdp, BPF_FIB_LOOKUP_OUTPUT); 311 | } 312 | 313 | __attribute__((__always_inline__)) static inline int 314 | action_end_m_gtp4_e(struct xdp_md *xdp, struct end_function *ef) 315 | { 316 | void *data_end = (void *)(unsigned long)xdp->data_end; 317 | void *data = (void *)(unsigned long)xdp->data; 318 | 319 | struct srhhdr *srh = get_srh(xdp); 320 | struct ipv6hdr *v6h = get_ipv6(xdp); 321 | __u16 payload_len, tot_len; 322 | 323 | if (!srh || !v6h) 324 | return XDP_PASS; 325 | 326 | int v6taple_key = 0; 327 | struct v6addr_heep *map_addr_taple = bpf_map_lookup_elem(&in_taple_v6_addr, &v6taple_key); 328 | if (!map_addr_taple) 329 | return XDP_DROP; 330 | 331 | map_addr_taple->saddr = v6h->saddr; 332 | map_addr_taple->daddr = v6h->daddr; 333 | // __builtin_memcpy(&outer_saddr, &v6h->saddr, sizeof(struct in6_addr)); 334 | // __builtin_memcpy(&outer_daddr, &v6h->daddr, sizeof(struct in6_addr)); 335 | payload_len = bpf_ntohs(v6h->payload_len) - (srh->hdrExtLen + 1) * 8; 336 | 337 | if (bpf_xdp_adjust_head(xdp, (int)(sizeof(struct ipv6hdr) + (srh->hdrExtLen + 1) * 8))) 338 | return XDP_PASS; 339 | 340 | if (bpf_xdp_adjust_head(xdp, 0 - (int)(sizeof(struct iphdr) + sizeof(struct udphdr) + sizeof(struct gtp1hdr)))) 341 | return XDP_PASS; 342 | 343 | data = (void *)(unsigned long)xdp->data; 344 | data_end = (void *)(unsigned long)xdp->data_end; 345 | 346 | struct ethhdr *new_eth = get_eth(xdp); 347 | struct iphdr *new_iph = get_ipv4(xdp); 348 | struct udphdr *new_uh = get_v4_udp(xdp); 349 | struct gtp1hdr *new_gtp1 = get_v4_gtp1(xdp); 350 | 351 | if (!new_eth || !new_iph || !new_uh || !new_gtp1) 352 | return XDP_DROP; 353 | 354 | tot_len = payload_len + sizeof(*new_iph) + sizeof(*new_uh) + sizeof(*new_gtp1); 355 | 356 | new_eth->h_proto = bpf_htons(ETH_P_IPV4); 357 | 358 | // write iphdr 359 | new_iph->version = 4; 360 | new_iph->ihl = sizeof(*new_iph) >> 2; 361 | new_iph->frag_off = bpf_htons(IP_DF); 362 | new_iph->protocol = IPPROTO_UDP; 363 | new_iph->check = 0; 364 | new_iph->tos = 0; 365 | new_iph->tot_len = bpf_htons(tot_len); 366 | new_iph->ttl = 64; 367 | 368 | struct args_mob_session args; 369 | __builtin_memset(&args, 0, sizeof(struct args_mob_session)); 370 | __u16 s_offset, s_shift, d_offset, d_shift; 371 | 372 | s_offset = ef->v4_addr_spos / 8; 373 | s_shift = ef->v4_addr_spos % 8; 374 | d_offset = ef->v4_addr_dpos / 8; 375 | d_shift = ef->v4_addr_dpos % 8; 376 | 377 | __u8 *src_p, *dst_p, *args_p; 378 | src_p = (__u8 *)&new_iph->saddr; 379 | dst_p = (__u8 *)&new_iph->daddr; 380 | args_p = (__u8 *)&args; 381 | 382 | read_v6addr_in_pkt_pyload(src_p, &map_addr_taple->saddr, sizeof(__u32), s_offset, s_shift, data_end); 383 | read_v6addr_in_pkt_pyload(dst_p, &map_addr_taple->daddr, sizeof(__u32), d_offset, d_shift, data_end); 384 | d_offset += sizeof(__u32); 385 | 386 | read_v6addr_in_pyload(args_p, &map_addr_taple->daddr, sizeof(struct args_mob_session), d_offset, d_shift); 387 | 388 | // write udphdr 389 | new_uh->check = 0; 390 | new_uh->source = bpf_htons(GTP1U_PORT); 391 | new_uh->dest = bpf_htons(GTP1U_PORT); 392 | new_uh->len = bpf_htons(payload_len + sizeof(struct udphdr) + sizeof(struct gtp1hdr)); 393 | 394 | // write gtp1hdr 395 | new_gtp1->flags = 0x30; 396 | 397 | new_gtp1->type = 0xff; 398 | new_gtp1->length = bpf_htons(payload_len); 399 | new_gtp1->tid = args.session.pdu_session_id; 400 | 401 | csum_build(new_iph); 402 | // ipv4_udp_csum_build(new_uh, new_iph, data_end); 403 | new_uh->check = 0; 404 | return rewrite_nexthop(xdp, 0); 405 | } 406 | SEC("xdp_prog") 407 | int srv6_handler(struct xdp_md *xdp) 408 | { 409 | // bpf_printk("exec thou\n"); 410 | void *data = (void *)(long)xdp->data; 411 | void *data_end = (void *)(long)xdp->data_end; 412 | struct ethhdr *eth = data; 413 | struct iphdr *iph = get_ipv4(xdp); 414 | struct ipv6hdr *v6h = get_ipv6(xdp); 415 | struct end_function *ef_table; 416 | struct transit_behavior *tb; 417 | struct lpm_key_v4 v4key; 418 | struct lpm_key_v6 v6key; 419 | __u16 h_proto; 420 | 421 | if (data + sizeof(*eth) > data_end) 422 | return xdpcap_exit(xdp, &xdpcap_hook, XDP_PASS); 423 | 424 | h_proto = eth->h_proto; 425 | if (h_proto == bpf_htons(ETH_P_IP)) 426 | { 427 | if (!iph) 428 | return xdpcap_exit(xdp, &xdpcap_hook, XDP_DROP); 429 | // use encap 430 | v4key.prefixlen = 32; 431 | v4key.addr = iph->daddr; 432 | 433 | tb = bpf_map_lookup_elem(&transit_table_v4, &v4key); 434 | if (tb) 435 | { 436 | // segment size valid 437 | switch (tb->action) 438 | { 439 | case SEG6_IPTUN_MODE_ENCAP: 440 | return xdpcap_exit(xdp, &xdpcap_hook, transit_encap(xdp, tb, IPPROTO_IPIP, bpf_ntohs(iph->tot_len))); 441 | 442 | case SEG6_IPTUN_MODE_ENCAP_H_M_GTP4_D: 443 | return xdpcap_exit(xdp, &xdpcap_hook, transit_gtp4_encap(xdp, tb)); 444 | } 445 | } 446 | } 447 | else if (h_proto == bpf_htons(ETH_P_IPV6)) 448 | { 449 | if (!v6h) 450 | return xdpcap_exit(xdp, &xdpcap_hook, XDP_DROP); 451 | 452 | v6key.prefixlen = 128; 453 | v6key.addr = v6h->daddr; 454 | if (v6h->nexthdr == NEXTHDR_ROUTING) 455 | { 456 | // use nexthop and exec to function or decap or encap 457 | ef_table = bpf_map_lookup_elem(&function_table, &v6key); 458 | if (ef_table) 459 | { 460 | switch (ef_table->function) 461 | { 462 | case SEG6_LOCAL_ACTION_END: 463 | return xdpcap_exit(xdp, &xdpcap_hook, action_end(xdp)); 464 | case SEG6_LOCAL_ACTION_END_DX4: 465 | return xdpcap_exit(xdp, &xdpcap_hook, action_enddx4(xdp, ef_table)); 466 | case SEG6_LOCAL_ACTION_END_M_GTP4_E: 467 | return xdpcap_exit(xdp, &xdpcap_hook, action_end_m_gtp4_e(xdp, ef_table)); 468 | } 469 | } 470 | } 471 | else 472 | { 473 | // encap type code 474 | tb = bpf_map_lookup_elem(&transit_table_v6, &v6key); 475 | if (tb) 476 | { 477 | // segment size valid 478 | switch (tb->action) 479 | { 480 | case SEG6_IPTUN_MODE_ENCAP: 481 | return xdpcap_exit(xdp, &xdpcap_hook, transit_encap(xdp, tb, IPPROTO_IPV6, v6h->payload_len)); 482 | // case SEG6_IPTUN_MODE_ENCAP_H_M_GTP4_D: 483 | // // bpf_printk("run SEG6_IPTUN_MODE_ENCAP_H_M_GTP4_D\n"); 484 | // return xdpcap_exit(xdp, &xdpcap_hook, action_h_gtp4_d(xdp, tb)); 485 | } 486 | } 487 | } 488 | } 489 | return xdpcap_exit(xdp, &xdpcap_hook, XDP_PASS); 490 | } 491 | 492 | SEC("xdp_pass") 493 | int xdp_pass_func(struct xdp_md *ctx) 494 | { 495 | return XDP_PASS; 496 | } 497 | char _license[] SEC("license") = "GPL"; 498 | -------------------------------------------------------------------------------- /src/srv6_consts.h: -------------------------------------------------------------------------------- 1 | #ifndef __SRV6_CONSTS_H 2 | #define __SRV6_CONSTS_H 3 | #include "bpf_endian.h" 4 | 5 | // user define fib ctl value 6 | #define NextFIBCheck 10000 7 | 8 | // max mtusize 9 | #define LOOP_MAX_RANGE 4000 10 | 11 | // linux/socket.h 12 | #define AF_INET 2 /* Internet IP Protocol */ 13 | #define AF_INET6 10 /* IP version 6 */ 14 | 15 | #define ETH_P_8021Q 0x8100 /* 802.1Q VLAN Extended Header */ 16 | #define ETH_P_8021AD 0x88A8 /* 802.1ad Service VLAN */ 17 | #define ETH_P_ARP 0x0806 18 | #define ETH_P_IPV4 0x0800 19 | #define ETH_P_IPV6 0x86DD 20 | 21 | // net/ipv6.h 22 | #define NEXTHDR_ROUTING 43 /* Routing header. */ 23 | 24 | // Entry size 25 | #define MAX_TXPORT_DEVICE 64 26 | #define MAX_TRANSIT_ENTRIES 256 27 | #define MAX_END_FUNCTION_ENTRIES 65536 28 | #define MAX_SEGMENTS 5 29 | #define MAX_GTP4_SRCADDR_PREFIX 96 30 | #define MAX_GTP4_DSTADDR_PREFIX 56 31 | 32 | // srh flag 33 | #define SR6_FLAG1_PROTECTED (1 << 6) 34 | #define SR6_FLAG1_OAM (1 << 5) 35 | #define SR6_FLAG1_ALERT (1 << 4) 36 | #define SR6_FLAG1_HMAC (1 << 3) 37 | 38 | #define SR6_TLV_INGRESS 1 39 | #define SR6_TLV_EGRESS 2 40 | #define SR6_TLV_OPAQUE 3 41 | #define SR6_TLV_PADDING 4 42 | #define SR6_TLV_HMAC 5 43 | 44 | #define sr_has_hmac(srh) ((srh)->flags & SR6_FLAG1_HMAC) 45 | 46 | //Encap define 47 | #define SEG6_IPTUN_MODE_INLINE 0 48 | #define SEG6_IPTUN_MODE_ENCAP 1 49 | #define SEG6_IPTUN_MODE_L2ENCAP 2 50 | #define SEG6_IPTUN_MODE_ENCAP_T_M_GTP6_D 3 51 | #define SEG6_IPTUN_MODE_ENCAP_T_M_GTP6_D_Di 4 52 | #define SEG6_IPTUN_MODE_ENCAP_H_M_GTP4_D 5 53 | 54 | // END($|X|T) case using 55 | #define SEG6_LOCAL_FLAVER_NONE 1 56 | #define SEG6_LOCAL_FLAVER_PSP 2 57 | #define SEG6_LOCAL_FLAVER_USP 3 58 | #define SEG6_LOCAL_FLAVER_USD 4 59 | 60 | // Function define(e.g. Decap, segleft...) 61 | #define SEG6_LOCAL_ACTION_END 1 62 | #define SEG6_LOCAL_ACTION_END_X 2 63 | #define SEG6_LOCAL_ACTION_END_T 3 64 | #define SEG6_LOCAL_ACTION_END_DX2 4 65 | #define SEG6_LOCAL_ACTION_END_DX6 5 66 | #define SEG6_LOCAL_ACTION_END_DX4 6 67 | #define SEG6_LOCAL_ACTION_END_DT6 7 68 | #define SEG6_LOCAL_ACTION_END_DT4 8 69 | #define SEG6_LOCAL_ACTION_END_B6 9 70 | #define SEG6_LOCAL_ACTION_END_B6_ENCAPS 10 71 | #define SEG6_LOCAL_ACTION_END_BM 11 72 | #define SEG6_LOCAL_ACTION_END_S 12 73 | #define SEG6_LOCAL_ACTION_END_AS 13 74 | #define SEG6_LOCAL_ACTION_END_AM 14 75 | #define SEG6_LOCAL_ACTION_END_M_GTP6_E 15 76 | #define SEG6_LOCAL_ACTION_END_M_GTP4_E 16 77 | 78 | #define IPV6_FLOWINFO_MASK __bpf_htonl(0x0FFFFFFF) 79 | 80 | // GTP User Data Messages (GTPv1) 81 | // 3GPP TS 29.060 "Table 1: Messages in GTP" 82 | #define GTPV1_ECHO 1 // Echo Request 83 | #define GTPV1_ECHORES 2 // Echo Response 84 | #define GTPV1_ERROR 26 // Error Indication 85 | #define GTPV1_END 254 // End Marker 86 | #define GTPV1_GPDU 255 // G-PDU 87 | 88 | #define GTP_V1 1 89 | #define GTP1U_PORT 2152 90 | 91 | /* from net/ip.h */ 92 | #define IP_DF 0x4000 /* Flag: "Don't Fragment" */ 93 | 94 | // from net/tcp.h 95 | #define TCPOPT_EOL 0 /* End of options */ 96 | #define TCPOPT_NOP 1 /* Padding */ 97 | #define TCPOPT_MSS 2 /* Segment size negotiating */ 98 | 99 | #define DEFAULT_TTL 255 100 | 101 | #endif 102 | -------------------------------------------------------------------------------- /src/srv6_helpers.h: -------------------------------------------------------------------------------- 1 | #ifndef __SRV6_HELPERS_H 2 | #define __SRV6_HELPERS_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "bpf_helpers.h" 16 | #include "bpf_endian.h" 17 | #include "srv6_consts.h" 18 | 19 | __attribute__((__always_inline__)) static inline void read_v6addr_in_pyload( 20 | __u8 *payload, struct in6_addr *v6addr, __u16 payload_size, __u16 offset, __u16 shift) 21 | { 22 | offset = offset & 0xf; 23 | payload_size = payload_size & 0xf; 24 | if (sizeof(struct in6_addr) <= offset || 25 | sizeof(struct in6_addr) <= payload_size + offset || 26 | offset < 0) 27 | return; 28 | 29 | if (shift == 0) 30 | { 31 | #pragma clang loop unroll(disable) 32 | for (__u16 index = 0; index < sizeof(struct in6_addr); index++) 33 | { 34 | index = index & 0xf; 35 | 36 | if (payload_size <= index) 37 | break; 38 | 39 | __u8 *payval1 = (__u8 *)(void *)payload + index; 40 | __u8 *v6val = (__u8 *)(void *)v6addr + offset + index; 41 | if (offset + index + 1 >= sizeof(struct in6_addr)) 42 | break; 43 | 44 | *payval1 = *v6val; 45 | } 46 | } 47 | else 48 | { 49 | #pragma clang loop unroll(disable) 50 | for (__u16 index = 0; index < sizeof(struct in6_addr); index++) 51 | { 52 | index = index & 0xf; 53 | 54 | if (payload_size <= index || payload_size <= index + 1) 55 | break; 56 | 57 | __u8 *payval1 = (__u8 *)(void *)payload + index; 58 | __u8 *payval2 = (__u8 *)(void *)payload + index + 1; 59 | __u8 *v6val = (__u8 *)(void *)v6addr + offset + index; 60 | if (offset + index + 1 >= sizeof(struct in6_addr)) 61 | break; 62 | 63 | *payval1 |= *v6val >> shift; 64 | *payval2 |= *v6val << (8 - shift); 65 | } 66 | } 67 | } 68 | 69 | __attribute__((__always_inline__)) static inline void read_v6addr_in_pkt_pyload( 70 | __u8 *payload, struct in6_addr *v6addr, __u16 payload_size, __u16 offset, __u16 shift, const __u32 *data_end) 71 | { 72 | offset = offset & 0xffff; 73 | payload_size = payload_size & 0xffff; 74 | if (sizeof(struct in6_addr) <= offset || 75 | sizeof(struct in6_addr) <= payload_size + offset || 76 | offset < 0) 77 | return; 78 | 79 | if (shift == 0) 80 | { 81 | if ((void *)payload + payload_size > data_end) 82 | return; 83 | 84 | __builtin_memcpy(payload, &v6addr->in6_u.u6_addr8[offset], payload_size); 85 | } 86 | else 87 | { 88 | #pragma clang loop unroll(disable) 89 | for (__u16 index = 0; index < sizeof(struct in6_addr); index++) 90 | { 91 | index = index & 0xf; 92 | 93 | if (payload_size <= index) 94 | break; 95 | 96 | __u8 *payval1 = (__u8 *)(void *)payload + index; 97 | __u8 *payval2 = (__u8 *)(void *)payload + index + 1; 98 | __u8 *v6val = (__u8 *)(void *)v6addr + offset + index; 99 | if ((void *)payval1 + 1 > data_end || payval2 + 1 > data_end || offset + index + 1 >= sizeof(struct in6_addr)) 100 | break; 101 | 102 | *payval1 |= *v6val >> shift; 103 | *payval2 |= *v6val << (8 - shift); 104 | } 105 | } 106 | } 107 | 108 | __attribute__((__always_inline__)) static inline void write_v6addr_in_pyload( 109 | struct in6_addr *v6addr, __u8 *payload, __u16 payload_size, __u16 offset, __u16 shift, const __u32 *data_end) 110 | { 111 | offset = offset & 0xfff; 112 | payload_size = payload_size & 0xffff; 113 | if (sizeof(struct in6_addr) <= offset || 114 | sizeof(struct in6_addr) <= payload_size + offset || 115 | offset < 0) 116 | return; 117 | 118 | if (shift == 0) 119 | { 120 | if ((void *)v6addr + offset + payload_size > data_end) 121 | return; 122 | 123 | __builtin_memcpy(&v6addr->in6_u.u6_addr8[offset], payload, payload_size); 124 | } 125 | else 126 | { 127 | #pragma clang loop unroll(disable) 128 | for (__u16 index = 0; index < sizeof(struct in6_addr); index++) 129 | { 130 | index = index & 0xf; 131 | 132 | if (payload_size <= index) 133 | break; 134 | 135 | __u8 *v6val1 = (__u8 *)(void *)v6addr + offset + index; 136 | __u8 *v6val2 = (__u8 *)(void *)v6addr + offset + index + 1; 137 | if (v6val1 + 1 <= data_end && v6val2 + 1 <= data_end) 138 | { 139 | *v6val1 |= payload[index] >> shift; 140 | *v6val2 |= payload[index] << (8 - shift); 141 | } 142 | else 143 | break; 144 | } 145 | } 146 | } 147 | 148 | /* from include/net/ip.h */ 149 | __attribute__((__always_inline__)) static inline int ip_decrease_ttl(struct iphdr *iph) 150 | { 151 | __u32 check = (__u32)iph->check; 152 | 153 | check += (__u32)bpf_htons(0x0100); 154 | iph->check = (__sum16)(check + (check >= 0xFFFF)); 155 | return --iph->ttl; 156 | }; 157 | __attribute__((__always_inline__)) static inline __u16 wrapsum(__u32 sum) 158 | { 159 | sum = ~sum & 0xFFFF; 160 | return (sum); 161 | } 162 | 163 | // cf. https://github.com/iovisor/bcc/issues/2463#issuecomment-718800510 164 | __attribute__((__always_inline__)) static inline void ipv4_udp_csum_build(struct udphdr *uh, struct iphdr *iph, __u32 *data_end) 165 | { 166 | uh->check = 0; 167 | __u32 csum = 0; 168 | 169 | csum = (iph->saddr >> 16) & 0xffff; 170 | csum += (iph->saddr) & 0xffff; 171 | csum += (iph->daddr >> 16) & 0xffff; 172 | csum += (iph->daddr) & 0xffff; 173 | csum += (iph->protocol) << 8; 174 | csum += uh->len; 175 | 176 | __u16 *buf = (__u16 *)uh; 177 | // Compute checksum on udp header + payload 178 | for (__u32 i = 0; i < LOOP_MAX_RANGE; i += 2) 179 | { 180 | if ((void *)(buf + 1) > data_end) 181 | break; 182 | 183 | csum += *buf; 184 | buf++; 185 | } 186 | if ((void *)buf + 1 <= data_end) 187 | // In case payload is not 2 bytes aligned 188 | csum += *(__u8 *)buf; 189 | 190 | csum = ~csum; 191 | uh->check = csum; 192 | // csum = (csum >> 16) + (csum & 0xffff); 193 | // csum += csum >> 16; 194 | // uh->check = wrapsum(csum); 195 | } 196 | 197 | __attribute__((__always_inline__)) static inline void csum_build(struct iphdr *iph) 198 | { 199 | __u16 *next_iph_u16; 200 | __u32 csum = 0; 201 | int i; 202 | iph->check = 0; 203 | next_iph_u16 = (__u16 *)iph; 204 | #pragma clang loop unroll(disable) 205 | for (i = 0; i < (sizeof(*iph) >> 1); i++) 206 | csum += *next_iph_u16++; 207 | 208 | iph->check = ~((csum & 0xffff) + (csum >> 16)); 209 | } 210 | 211 | /* Function to set source and destination mac of the packet */ 212 | __attribute__((__always_inline__)) static inline void set_src_dst_mac(void *data, void *src, void *dst) 213 | { 214 | unsigned short *source = src; 215 | unsigned short *dest = dst; 216 | unsigned short *p = data; 217 | 218 | __builtin_memcpy(p, dest, ETH_ALEN); 219 | __builtin_memcpy(p + 3, source, ETH_ALEN); 220 | } 221 | 222 | __attribute__((__always_inline__)) static inline struct ethhdr *get_eth(struct xdp_md *xdp) 223 | { 224 | void *data = (void *)(long)xdp->data; 225 | void *data_end = (void *)(long)xdp->data_end; 226 | 227 | struct ethhdr *eth = data; 228 | if (eth + 1 > data_end) 229 | return NULL; 230 | 231 | return eth; 232 | } 233 | __attribute__((__always_inline__)) static inline struct ipv6hdr *get_ipv6(struct xdp_md *xdp) 234 | { 235 | void *data = (void *)(long)xdp->data; 236 | void *data_end = (void *)(long)xdp->data_end; 237 | 238 | struct ipv6hdr *v6h = data + sizeof(struct ethhdr); 239 | 240 | if (v6h + 1 > data_end) 241 | return NULL; 242 | 243 | return v6h; 244 | }; 245 | 246 | __attribute__((__always_inline__)) static inline struct iphdr *get_ipv4(struct xdp_md *xdp) 247 | { 248 | void *data = (void *)(long)xdp->data; 249 | void *data_end = (void *)(long)xdp->data_end; 250 | 251 | struct iphdr *iph = data + sizeof(struct ethhdr); 252 | if (iph + 1 > data_end) 253 | return NULL; 254 | 255 | return iph; 256 | }; 257 | 258 | __attribute__((__always_inline__)) static inline struct srhhdr *get_srh(struct xdp_md *xdp) 259 | { 260 | void *data = (void *)(long)xdp->data; 261 | void *data_end = (void *)(long)xdp->data_end; 262 | 263 | struct srhhdr *srh; 264 | int len, srhoff = 0; 265 | 266 | srh = data + sizeof(struct ethhdr) + sizeof(struct ipv6hdr); 267 | if (srh + 1 > data_end) 268 | { 269 | return NULL; 270 | } 271 | // len = (srh->hdrlen + 1) << 3; 272 | 273 | return srh; 274 | } 275 | 276 | __attribute__((__always_inline__)) static inline struct udphdr *get_v4_udp(struct xdp_md *xdp) 277 | { 278 | void *data = (void *)(long)xdp->data; 279 | void *data_end = (void *)(long)xdp->data_end; 280 | 281 | struct udphdr *uh = data + sizeof(struct ethhdr) + sizeof(struct iphdr); 282 | 283 | if ((void *)uh + sizeof(struct udphdr) > data_end) 284 | return NULL; 285 | 286 | return uh; 287 | }; 288 | 289 | __attribute__((__always_inline__)) static inline struct gtp1hdr *get_v4_gtp1(struct xdp_md *xdp) 290 | { 291 | void *data = (void *)(long)xdp->data; 292 | void *data_end = (void *)(long)xdp->data_end; 293 | 294 | struct gtp1hdr *gtp1 = data + sizeof(struct ethhdr) + sizeof(struct iphdr) + sizeof(struct udphdr); 295 | 296 | if ((void *)gtp1 + sizeof(struct gtp1hdr) > data_end) 297 | return NULL; 298 | 299 | return gtp1; 300 | }; 301 | 302 | __attribute__((__always_inline__)) static inline struct srhhdr *get_and_validate_srh(struct xdp_md *xdp) 303 | { 304 | struct srhhdr *srh; 305 | 306 | srh = get_srh(xdp); 307 | if (!srh) 308 | return NULL; 309 | 310 | if (srh->segmentsLeft == 0) 311 | return NULL; 312 | 313 | // TODO 314 | // #ifdef CONFIG_IPV6_SEG6_HMAC 315 | // if (!seg6_hmac_validate_skb(skb)) 316 | // return NULL; 317 | // #endif 318 | 319 | return srh; 320 | } 321 | 322 | __attribute__((__always_inline__)) static inline bool advance_nextseg(struct srhhdr *srh, struct in6_addr *daddr, struct xdp_md *xdp) 323 | { 324 | struct in6_addr *addr; 325 | void *data_end = (void *)(long)xdp->data_end; 326 | 327 | srh->segmentsLeft--; 328 | if ((void *)(long)srh + sizeof(struct srhhdr) + sizeof(struct in6_addr) * (srh->segmentsLeft + 1) > data_end) 329 | return false; 330 | 331 | addr = srh->segments + srh->segmentsLeft; 332 | if (addr + 1 > data_end) 333 | return false; 334 | 335 | *daddr = *addr; 336 | return true; 337 | } 338 | 339 | __attribute__((__always_inline__)) static inline bool lookup_nexthop(struct xdp_md *xdp, void *smac, void *dmac, __u32 *ifindex, __u32 flag) 340 | { 341 | void *data = (void *)(long)xdp->data; 342 | void *data_end = (void *)(long)xdp->data_end; 343 | struct ethhdr *eth = data; 344 | struct iphdr *iph = get_ipv4(xdp); 345 | struct ipv6hdr *v6h = get_ipv6(xdp); 346 | struct bpf_fib_lookup fib_params = {}; 347 | __u16 h_proto; 348 | 349 | //TODO:: impl dot1q proto 350 | if (data + sizeof(struct ethhdr) > data_end) 351 | return false; 352 | 353 | h_proto = eth->h_proto; 354 | __builtin_memset(&fib_params, 0, sizeof(fib_params)); 355 | 356 | switch (h_proto) 357 | { 358 | case bpf_htons(ETH_P_IP): 359 | if (!iph) 360 | return false; 361 | 362 | fib_params.family = AF_INET; 363 | fib_params.tos = iph->tos; 364 | fib_params.l4_protocol = iph->protocol; 365 | fib_params.sport = 0; 366 | fib_params.dport = 0; 367 | fib_params.tot_len = bpf_ntohs(iph->tot_len); 368 | fib_params.ipv4_src = iph->saddr; 369 | fib_params.ipv4_dst = iph->daddr; 370 | break; 371 | 372 | case bpf_htons(ETH_P_IPV6): 373 | if (!v6h) 374 | return false; 375 | 376 | if (v6h->hop_limit <= 1) 377 | return false; 378 | 379 | struct in6_addr *src = (struct in6_addr *)fib_params.ipv6_src; 380 | struct in6_addr *dst = (struct in6_addr *)fib_params.ipv6_dst; 381 | 382 | fib_params.family = AF_INET6; 383 | fib_params.tos = 0; 384 | fib_params.flowinfo = *(__be32 *)v6h & IPV6_FLOWINFO_MASK; 385 | fib_params.l4_protocol = v6h->nexthdr; 386 | fib_params.sport = 0; 387 | fib_params.dport = 0; 388 | fib_params.tot_len = bpf_ntohs(v6h->payload_len); 389 | *src = v6h->saddr; 390 | *dst = v6h->daddr; 391 | break; 392 | 393 | default: 394 | return false; 395 | } 396 | 397 | // bpf_fib_lookup 398 | // flags: BPF_FIB_LOOKUP_DIRECT, BPF_FIB_LOOKUP_OUTPUT 399 | // https://github.com/torvalds/linux/blob/v4.18/include/uapi/linux/bpf.h#L2611 400 | fib_params.ifindex = xdp->ingress_ifindex; 401 | // int rc = bpf_fib_lookup(xdp, &fib_params, sizeof(fib_params), BPF_FIB_LOOKUP_DIRECT | BPF_FIB_LOOKUP_OUTPUT); 402 | int rc = bpf_fib_lookup(xdp, &fib_params, sizeof(fib_params), flag); 403 | 404 | switch (rc) 405 | { 406 | case BPF_FIB_LKUP_RET_SUCCESS: /* lookup successful */ 407 | if (h_proto == bpf_htons(ETH_P_IP)) 408 | ip_decrease_ttl(iph); 409 | else if (h_proto == bpf_htons(ETH_P_IPV6)) 410 | v6h->hop_limit--; 411 | 412 | *ifindex = fib_params.ifindex; 413 | 414 | __u8 *source = smac; 415 | __u8 *dest = dmac; 416 | __builtin_memcpy(dest, fib_params.dmac, ETH_ALEN); 417 | __builtin_memcpy(source, fib_params.smac, ETH_ALEN); 418 | return true; 419 | case BPF_FIB_LKUP_RET_BLACKHOLE: /* dest is blackholed; can be dropped */ 420 | // bpf_printk("BPF_FIB_LKUP_RET_BLACKHOLE"); 421 | break; 422 | case BPF_FIB_LKUP_RET_UNREACHABLE: /* dest is unreachable; can be dropped */ 423 | // bpf_printk("BPF_FIB_LKUP_RET_UNREACHABLE"); 424 | break; 425 | case BPF_FIB_LKUP_RET_PROHIBIT: /* dest not allowed; can be dropped */ 426 | // bpf_printk("BPF_FIB_LKUP_RET_PROHIBIT"); 427 | break; 428 | // action = XDP_DROP; 429 | // return false; 430 | case BPF_FIB_LKUP_RET_NOT_FWDED: /* packet is not forwarded */ 431 | // bpf_printk("BPF_FIB_LKUP_RET_NOT_FWDED"); 432 | break; 433 | case BPF_FIB_LKUP_RET_FWD_DISABLED: /* fwding is not enabled on ingress */ 434 | // bpf_printk("BPF_FIB_LKUP_RET_FWD_DISABLED"); 435 | break; 436 | case BPF_FIB_LKUP_RET_UNSUPP_LWT: /* fwd requires encapsulation */ 437 | // bpf_printk("BPF_FIB_LKUP_RET_UNSUPP_LWT"); 438 | break; 439 | case BPF_FIB_LKUP_RET_NO_NEIGH: /* no neighbor entry for nh */ 440 | // bpf_printk("BPF_FIB_LKUP_RET_NO_NEIGH"); 441 | break; 442 | case BPF_FIB_LKUP_RET_FRAG_NEEDED: /* fragmentation required to fwd */ 443 | // bpf_printk("BPF_FIB_LKUP_RET_FRAG_NEEDED"); 444 | break; 445 | /* PASS */ 446 | return false; 447 | } 448 | return false; 449 | } 450 | 451 | __attribute__((__always_inline__)) static inline int rewrite_nexthop(struct xdp_md *xdp, __u32 flag) 452 | { 453 | void *data = (void *)(long)xdp->data; 454 | void *data_end = (void *)(long)xdp->data_end; 455 | struct ethhdr *eth = data; 456 | 457 | if (data + 1 > data_end) 458 | return XDP_PASS; 459 | 460 | // bpf_printk("rewrite_nexthop"); 461 | 462 | __u32 ifindex; 463 | __u8 smac[6], dmac[6]; 464 | 465 | bool is_exist = lookup_nexthop(xdp, &smac, &dmac, &ifindex, flag); 466 | if (is_exist) 467 | { 468 | set_src_dst_mac(data, &smac, &dmac); 469 | // bpf_printk("lockup"); 470 | if (!bpf_map_lookup_elem(&tx_port, &ifindex)) 471 | return XDP_PASS; 472 | 473 | if (xdp->ingress_ifindex == ifindex) 474 | { 475 | // bpf_printk("run tx"); 476 | return XDP_TX; 477 | } 478 | // bpf_printk("go to redirect"); 479 | return bpf_redirect_map(&tx_port, ifindex, 0); 480 | } 481 | // bpf_printk("failed rewrite nhop"); 482 | return XDP_PASS; 483 | } 484 | 485 | #endif 486 | -------------------------------------------------------------------------------- /src/srv6_maps.h: -------------------------------------------------------------------------------- 1 | #ifndef __PGWU_MAPS_H 2 | #define __PGWU_MAPS_H 3 | 4 | #include 5 | #include "bpf_helpers.h" 6 | #include "srv6_consts.h" 7 | #include "srv6_structs.h" 8 | #include "hook.h" 9 | 10 | struct 11 | { 12 | __uint(type, BPF_MAP_TYPE_DEVMAP); 13 | __uint(key_size, sizeof(int)); 14 | __uint(value_size, sizeof(int)); 15 | __uint(max_entries, MAX_TXPORT_DEVICE); 16 | } tx_port SEC(".maps"); 17 | 18 | struct 19 | { 20 | __uint(type, BPF_MAP_TYPE_LPM_TRIE); 21 | __type(key, struct lpm_key_v6); 22 | __type(value, struct end_function); 23 | __uint(max_entries, MAX_END_FUNCTION_ENTRIES); 24 | __uint(map_flags, BPF_F_NO_PREALLOC); 25 | } function_table SEC(".maps"); 26 | 27 | struct 28 | { 29 | __uint(type, BPF_MAP_TYPE_LPM_TRIE); 30 | __type(key, struct lpm_key_v4); 31 | __type(value, struct transit_behavior); 32 | __uint(max_entries, MAX_TRANSIT_ENTRIES); 33 | __uint(map_flags, BPF_F_NO_PREALLOC); 34 | } transit_table_v4 SEC(".maps"); 35 | 36 | struct 37 | { 38 | __uint(type, BPF_MAP_TYPE_LPM_TRIE); 39 | __type(key, struct lpm_key_v6); 40 | __type(value, struct transit_behavior); 41 | __uint(max_entries, MAX_TRANSIT_ENTRIES); 42 | __uint(map_flags, BPF_F_NO_PREALLOC); 43 | } transit_table_v6 SEC(".maps"); 44 | 45 | struct 46 | { 47 | __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY); 48 | __type(key, int); 49 | __type(value, struct v6addr_heep); 50 | __uint(max_entries, 1); 51 | } in_taple_v6_addr SEC(".maps"); 52 | 53 | // https://github.com/cloudflare/xdpcap 54 | // struct bpf_map_def SEC("maps") xdpcap_hook = XDPCAP_HOOK(); 55 | struct xdpcap_hook 56 | { 57 | __uint(type, BPF_MAP_TYPE_PROG_ARRAY); 58 | __uint(key_size, sizeof(int)); 59 | __uint(value_size, sizeof(int)); 60 | __uint(max_entries, 5); 61 | } xdpcap_hook SEC(".maps"); 62 | 63 | #endif 64 | -------------------------------------------------------------------------------- /src/srv6_structs.h: -------------------------------------------------------------------------------- 1 | #ifndef __SRV6_STRUCTS_H 2 | #define __SRV6_STRUCTS_H 3 | #include "srv6_consts.h" 4 | 5 | #include 6 | #include /* For struct in6_addr. */ 7 | 8 | struct v6addr_heep 9 | { 10 | struct in6_addr saddr; 11 | struct in6_addr daddr; 12 | }; 13 | 14 | struct transit_behavior 15 | { 16 | struct in6_addr saddr; 17 | struct in6_addr daddr; 18 | __u32 s_prefixlen; 19 | __u32 d_prefixlen; 20 | __u32 segment_length; 21 | __u32 action; 22 | struct in6_addr segments[MAX_SEGMENTS]; 23 | }; 24 | 25 | struct lpm_key_v4 26 | { 27 | __u32 prefixlen; 28 | __u32 addr; 29 | }; 30 | 31 | struct lpm_key_v6 32 | { 33 | __u32 prefixlen; 34 | struct in6_addr addr; 35 | }; 36 | 37 | struct end_function 38 | { 39 | __u32 saddr[4]; 40 | union 41 | { 42 | __u32 v6addr[4]; 43 | struct v4 44 | { 45 | __u32 addr; 46 | __u32 padding[3]; 47 | } v4; 48 | } nexthop; 49 | // The reason why the "__u32 function" is not "__u8" is that it also serves as padding. 50 | // The cilium/ebpf package assumes that the go structure takes 4 bytes each and does not pack. 51 | __u32 function; 52 | __u32 flaver; 53 | __u32 v4_addr_spos; 54 | __u32 v4_addr_dpos; 55 | }; 56 | 57 | // Segment Routing Extension Header (SRH) 58 | // https://datatracker.ietf.org/doc/draft-ietf-6man-segment-routing-header/ 59 | struct srhhdr 60 | { 61 | __u8 nextHdr; 62 | __u8 hdrExtLen; 63 | __u8 routingType; 64 | __u8 segmentsLeft; 65 | __u8 lastEntry; 66 | __u8 flags; 67 | __u16 tag; 68 | // cf. 5.3. Encoding of Tags Field/ https://datatracker.ietf.org/doc/draft-murakami-dmm-user-plane-message-encoding 69 | // __u8 gtpMessageType : 4; // least significant 4 bits of tag field 70 | struct in6_addr segments[0]; 71 | }; 72 | 73 | /* 74 | * struct vlan_hdr - vlan header 75 | * @h_vlan_TCI: priority and VLAN ID 76 | * @h_vlan_encapsulated_proto: packet type ID or len 77 | */ 78 | struct vlan_hdr 79 | { 80 | __u16 h_vlan_TCI; 81 | __u16 h_vlan_encapsulated_proto; 82 | }; 83 | 84 | // struct gtpu_exthdr 85 | // { 86 | // __u16 seq; 87 | // __u8 npdu_num; 88 | // __u8 nextexthdr; 89 | // }; 90 | 91 | // struct gtp1_pdu_session_t 92 | // { 93 | // __u8 exthdrlen; 94 | // __u8 type : 4; 95 | // __u8 spare : 4; 96 | // union 97 | // { 98 | // struct gtpu_qfi_bits 99 | // { 100 | // __u8 p : 1; 101 | // __u8 r : 1; 102 | // __u8 qfi : 6; 103 | // } bits; 104 | 105 | // __u8 val; 106 | // } u; 107 | 108 | // struct gtpu_exthdr paging[0]; 109 | // __u8 nextexthdr; 110 | // }; 111 | 112 | /* According to 3GPP TS 29.060. */ 113 | struct gtp1hdr 114 | { 115 | // __u8 version : 3; // Version field: always 1 for GTPv1 116 | // __u8 pt : 1; // Protocol Type (PT): GTP(1), GTP'(0) 117 | // __u8 reserved : 1; // always zero (0) 118 | // __u8 e : 1; // Extension Header flag (E) 119 | // __u8 s : 1; // Sequence number flag (S): not present(0), present(1) 120 | // __u8 pn : 1; // N-PDU Number flag (PN) 121 | __u8 flags; 122 | __u8 type; 123 | __u16 length; 124 | __u32 tid; 125 | 126 | // options 127 | // __u16 seq; // Sequence Number 128 | // __u8 npdu; // N-PDU number 129 | // __u8 nextExtHdr; // Next Extention Header Type 130 | }; 131 | 132 | // https://tools.ietf.org/html/draft-ietf-dmm-srv6-mobile-uplane-09#section-6.1 133 | // https://tools.ietf.org/html/draft-murakami-dmm-user-plane-message-encoding-02#section-5.2 134 | struct args_mob_session 135 | { 136 | __u8 qfi : 6; 137 | __u8 r : 1; 138 | __u8 u : 1; 139 | // __u8 qfi_r_u; 140 | union 141 | { 142 | __u32 pdu_session_id; 143 | struct seq 144 | { 145 | __u16 seq; 146 | __u16 padding; 147 | } seq; 148 | } session; 149 | } __attribute__((packed)); 150 | 151 | #endif 152 | -------------------------------------------------------------------------------- /vinbero.yml: -------------------------------------------------------------------------------- 1 | internal: 2 | # logfile: "/var/log/vinbero.log" 3 | logfile: "./vinbero.log" 4 | development: false 5 | devices: 6 | - ens4f0 7 | - ens4f1 8 | settings: 9 | # functions: 10 | # - action: SEG6_LOCAL_ACTION_END_DX4 11 | # triggerAddr: fc00:2::2/128 12 | # nexthop: 10.2.0.1 13 | # functions: 14 | # - action: SEG6_LOCAL_ACTION_END 15 | # triggerAddr: fc00:2::2/128 16 | # # actionSrcAddr: fc00:1::1 #optional 17 | # flaver: NONE 18 | # - action: SEG6_LOCAL_ACTION_END_DX4 19 | # addr: fc00:3::3/128 20 | # nexthop: 172.0.2.1 21 | # actionSrcAddr: 172.0.1.2 22 | # - action: SEG6_LOCAL_ACTION_END_M_GTP4_E 23 | # triggerAddr: fc00:2::/48 24 | # actionSrcAddr: 10.0.2.2 25 | # v4AddrSPos: 64 26 | # v4AddrDPos: 48 27 | transitv4: 28 | - action: SEG6_IPTUN_MODE_ENCAP_H_M_GTP4_D 29 | triggerAddr: 10.2.0.1/24 30 | actionSrcAddr: fc00:2::/64 31 | actionDstAddr: fc00:3::/48 # last arrive next hop 32 | segments: 33 | - fc00:3::3 34 | # - action: SEG6_IPTUN_MODE_ENCAP 35 | # triggerAddr: 10.2.0.0/24 36 | # actionSrcAddr: fc00:2::2 37 | # segments: # max size 5 38 | # - fc00:3::3 # last arrive next hop 39 | 40 | # transitv6: 41 | # - action: SEG6_IPTUN_MODE_ENCAP 42 | # addr: fd00:1::1/64 43 | # actionSrcAddr: fc00:1::1 44 | # segments: 45 | # - fc00:2::1 46 | # - fc00:3::3 47 | -------------------------------------------------------------------------------- /vinbero.yml.sample: -------------------------------------------------------------------------------- 1 | # This is an explanation of what each parameter means. 2 | internal: 3 | logfile: "./vinbero.log" # logging file output place 4 | development: false # devlopting only. loglevel up and packetdump ... etc meaning 5 | devices: # nic interface name 6 | - eth1 7 | - eth2 8 | settings: 9 | functions: 10 | - action: SEG6_LOCAL_ACTION_END 11 | triggerAddr: fc00:2::1/128 12 | actionSrcAddr: fc00:1::1 #optional 13 | flaver: NONE 14 | - action: SEG6_LOCAL_ACTION_END_DX6 15 | triggerAddr: fc00:2::1/128 16 | nexthop: fd00:2::1 17 | actionSrcAddr: fd00:2::2 18 | - action: SEG6_LOCAL_ACTION_END_DX4 19 | triggerAddr: fc00:2::1/128 20 | nexthop: 172.0.1.1 21 | - action: SEG6_LOCAL_ACTION_END_DT4 # not impli 22 | triggerAddr: fc00:2::1/128 23 | nexthop: 172.0.1.0/24 24 | - action: SEG6_LOCAL_ACTION_END_M_GTP4_E 25 | triggerAddr: fc00:2::1/128 26 | v4AddrPos: 64 27 | - action: SEG6_LOCAL_ACTION_END_M_GTP6_E 28 | triggerAddr: fc00:2::1/128 29 | transitv4: 30 | - action: SEG6_IPTUN_MODE_ENCAP 31 | triggerAddr: 172.0.2.0/24 32 | actionSrcAddr: fc00:1::1 33 | segments: # max size 5 34 | - fc00:3::3 # last arrive next hop 35 | - 36 | - fc00:2::1 # fast arrive next hop 37 | - action: SEG6_IPTUN_MODE_ENCAP_H_M_GTP4_D 38 | triggerAddr: 172.0.1.0/24 39 | actionSrcAddr: fc00:1::/64 # subnet range 0=>94 40 | actionDstAddr: fc00:3::/48 # last arrive next hop, subnet range 0=>56 41 | segments: # max size 4 42 | - fc00:1::1 43 | - 44 | - fc00:2::1 45 | transitv6: 46 | - action: SEG6_IPTUN_MODE_ENCAP 47 | triggerAddr: fd00:1::1/64 48 | actionSrcAddr: fc00:1::1 49 | segments: 50 | - fc00:2::1 51 | - fc00:3::3 52 | - action: SEG6_IPTUN_MODE_ENCAP_T_M_GTP6_D 53 | triggerAddr: 172.0.1.0/24 54 | actionSrcAddr: fc00:1::/64 55 | actionDstAddr: fc00:3::/64 # last arrive next hop 56 | segments: 57 | - fc00:1::1 58 | - 59 | - fc00:2::1 60 | --------------------------------------------------------------------------------