initial
This commit is contained in:
8
.gitignore
vendored
Normal file
8
.gitignore
vendored
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
build/
|
||||||
|
.DS_Store
|
||||||
|
*.o
|
||||||
|
*.obj
|
||||||
|
*.so
|
||||||
|
*.dll
|
||||||
|
*.dylib
|
||||||
|
*.exe
|
||||||
43
CMakeLists.txt
Normal file
43
CMakeLists.txt
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.16)
|
||||||
|
|
||||||
|
project(miniroute
|
||||||
|
VERSION 0.1.0
|
||||||
|
LANGUAGES C
|
||||||
|
)
|
||||||
|
|
||||||
|
set(CMAKE_C_STANDARD 23)
|
||||||
|
set(CMAKE_C_STANDARD_REQUIRED ON)
|
||||||
|
set(CMAKE_C_EXTENSIONS OFF)
|
||||||
|
|
||||||
|
find_package(Threads REQUIRED)
|
||||||
|
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
# Output directories
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
|
||||||
|
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
|
||||||
|
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
|
||||||
|
|
||||||
|
foreach(OUTPUTCONFIG DEBUG RELEASE RELWITHDEBINFO MINSIZEREL)
|
||||||
|
string(TOUPPER ${OUTPUTCONFIG} OUTPUTCONFIG_UPPER)
|
||||||
|
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_${OUTPUTCONFIG_UPPER} ${CMAKE_BINARY_DIR}/bin)
|
||||||
|
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_${OUTPUTCONFIG_UPPER} ${CMAKE_BINARY_DIR}/lib)
|
||||||
|
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_${OUTPUTCONFIG_UPPER} ${CMAKE_BINARY_DIR}/lib)
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
# Exec
|
||||||
|
file(GLOB_RECURSE SERVER_SRC CONFIGURE_DEPENDS src/*.c)
|
||||||
|
add_executable(miniroute ${SERVER_SRC})
|
||||||
|
target_link_libraries(miniroute PRIVATE ${CMAKE_THREAD_LIBS_INIT})
|
||||||
|
target_include_directories(miniroute PRIVATE
|
||||||
|
${PROJECT_SOURCE_DIR}/include
|
||||||
|
)
|
||||||
|
target_compile_options(miniroute PRIVATE
|
||||||
|
-Wall
|
||||||
|
-Wextra
|
||||||
|
-Wpedantic
|
||||||
|
-g
|
||||||
|
)
|
||||||
|
target_compile_definitions(miniroute PRIVATE)
|
||||||
|
set_target_properties(miniroute PROPERTIES OUTPUT_NAME "miniroute")
|
||||||
|
|
||||||
338
LICENSE
Normal file
338
LICENSE
Normal file
@@ -0,0 +1,338 @@
|
|||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
Version 2, June 1991
|
||||||
|
|
||||||
|
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||||
|
<https://fsf.org/>
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
Preamble
|
||||||
|
|
||||||
|
The licenses for most software are designed to take away your
|
||||||
|
freedom to share and change it. By contrast, the GNU General Public
|
||||||
|
License is intended to guarantee your freedom to share and change free
|
||||||
|
software--to make sure the software is free for all its users. This
|
||||||
|
General Public License applies to most of the Free Software
|
||||||
|
Foundation's software and to any other program whose authors commit to
|
||||||
|
using it. (Some other Free Software Foundation software is covered by
|
||||||
|
the GNU Lesser General Public License instead.) You can apply it to
|
||||||
|
your programs, too.
|
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom, not
|
||||||
|
price. Our General Public Licenses are designed to make sure that you
|
||||||
|
have the freedom to distribute copies of free software (and charge for
|
||||||
|
this service if you wish), that you receive source code or can get it
|
||||||
|
if you want it, that you can change the software or use pieces of it
|
||||||
|
in new free programs; and that you know you can do these things.
|
||||||
|
|
||||||
|
To protect your rights, we need to make restrictions that forbid
|
||||||
|
anyone to deny you these rights or to ask you to surrender the rights.
|
||||||
|
These restrictions translate to certain responsibilities for you if you
|
||||||
|
distribute copies of the software, or if you modify it.
|
||||||
|
|
||||||
|
For example, if you distribute copies of such a program, whether
|
||||||
|
gratis or for a fee, you must give the recipients all the rights that
|
||||||
|
you have. You must make sure that they, too, receive or can get the
|
||||||
|
source code. And you must show them these terms so they know their
|
||||||
|
rights.
|
||||||
|
|
||||||
|
We protect your rights with two steps: (1) copyright the software, and
|
||||||
|
(2) offer you this license which gives you legal permission to copy,
|
||||||
|
distribute and/or modify the software.
|
||||||
|
|
||||||
|
Also, for each author's protection and ours, we want to make certain
|
||||||
|
that everyone understands that there is no warranty for this free
|
||||||
|
software. If the software is modified by someone else and passed on, we
|
||||||
|
want its recipients to know that what they have is not the original, so
|
||||||
|
that any problems introduced by others will not reflect on the original
|
||||||
|
authors' reputations.
|
||||||
|
|
||||||
|
Finally, any free program is threatened constantly by software
|
||||||
|
patents. We wish to avoid the danger that redistributors of a free
|
||||||
|
program will individually obtain patent licenses, in effect making the
|
||||||
|
program proprietary. To prevent this, we have made it clear that any
|
||||||
|
patent must be licensed for everyone's free use or not licensed at all.
|
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and
|
||||||
|
modification follow.
|
||||||
|
|
||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
|
||||||
|
0. This License applies to any program or other work which contains
|
||||||
|
a notice placed by the copyright holder saying it may be distributed
|
||||||
|
under the terms of this General Public License. The "Program", below,
|
||||||
|
refers to any such program or work, and a "work based on the Program"
|
||||||
|
means either the Program or any derivative work under copyright law:
|
||||||
|
that is to say, a work containing the Program or a portion of it,
|
||||||
|
either verbatim or with modifications and/or translated into another
|
||||||
|
language. (Hereinafter, translation is included without limitation in
|
||||||
|
the term "modification".) Each licensee is addressed as "you".
|
||||||
|
|
||||||
|
Activities other than copying, distribution and modification are not
|
||||||
|
covered by this License; they are outside its scope. The act of
|
||||||
|
running the Program is not restricted, and the output from the Program
|
||||||
|
is covered only if its contents constitute a work based on the
|
||||||
|
Program (independent of having been made by running the Program).
|
||||||
|
Whether that is true depends on what the Program does.
|
||||||
|
|
||||||
|
1. You may copy and distribute verbatim copies of the Program's
|
||||||
|
source code as you receive it, in any medium, provided that you
|
||||||
|
conspicuously and appropriately publish on each copy an appropriate
|
||||||
|
copyright notice and disclaimer of warranty; keep intact all the
|
||||||
|
notices that refer to this License and to the absence of any warranty;
|
||||||
|
and give any other recipients of the Program a copy of this License
|
||||||
|
along with the Program.
|
||||||
|
|
||||||
|
You may charge a fee for the physical act of transferring a copy, and
|
||||||
|
you may at your option offer warranty protection in exchange for a fee.
|
||||||
|
|
||||||
|
2. You may modify your copy or copies of the Program or any portion
|
||||||
|
of it, thus forming a work based on the Program, and copy and
|
||||||
|
distribute such modifications or work under the terms of Section 1
|
||||||
|
above, provided that you also meet all of these conditions:
|
||||||
|
|
||||||
|
a) You must cause the modified files to carry prominent notices
|
||||||
|
stating that you changed the files and the date of any change.
|
||||||
|
|
||||||
|
b) You must cause any work that you distribute or publish, that in
|
||||||
|
whole or in part contains or is derived from the Program or any
|
||||||
|
part thereof, to be licensed as a whole at no charge to all third
|
||||||
|
parties under the terms of this License.
|
||||||
|
|
||||||
|
c) If the modified program normally reads commands interactively
|
||||||
|
when run, you must cause it, when started running for such
|
||||||
|
interactive use in the most ordinary way, to print or display an
|
||||||
|
announcement including an appropriate copyright notice and a
|
||||||
|
notice that there is no warranty (or else, saying that you provide
|
||||||
|
a warranty) and that users may redistribute the program under
|
||||||
|
these conditions, and telling the user how to view a copy of this
|
||||||
|
License. (Exception: if the Program itself is interactive but
|
||||||
|
does not normally print such an announcement, your work based on
|
||||||
|
the Program is not required to print an announcement.)
|
||||||
|
|
||||||
|
These requirements apply to the modified work as a whole. If
|
||||||
|
identifiable sections of that work are not derived from the Program,
|
||||||
|
and can be reasonably considered independent and separate works in
|
||||||
|
themselves, then this License, and its terms, do not apply to those
|
||||||
|
sections when you distribute them as separate works. But when you
|
||||||
|
distribute the same sections as part of a whole which is a work based
|
||||||
|
on the Program, the distribution of the whole must be on the terms of
|
||||||
|
this License, whose permissions for other licensees extend to the
|
||||||
|
entire whole, and thus to each and every part regardless of who wrote it.
|
||||||
|
|
||||||
|
Thus, it is not the intent of this section to claim rights or contest
|
||||||
|
your rights to work written entirely by you; rather, the intent is to
|
||||||
|
exercise the right to control the distribution of derivative or
|
||||||
|
collective works based on the Program.
|
||||||
|
|
||||||
|
In addition, mere aggregation of another work not based on the Program
|
||||||
|
with the Program (or with a work based on the Program) on a volume of
|
||||||
|
a storage or distribution medium does not bring the other work under
|
||||||
|
the scope of this License.
|
||||||
|
|
||||||
|
3. You may copy and distribute the Program (or a work based on it,
|
||||||
|
under Section 2) in object code or executable form under the terms of
|
||||||
|
Sections 1 and 2 above provided that you also do one of the following:
|
||||||
|
|
||||||
|
a) Accompany it with the complete corresponding machine-readable
|
||||||
|
source code, which must be distributed under the terms of Sections
|
||||||
|
1 and 2 above on a medium customarily used for software interchange; or,
|
||||||
|
|
||||||
|
b) Accompany it with a written offer, valid for at least three
|
||||||
|
years, to give any third party, for a charge no more than your
|
||||||
|
cost of physically performing source distribution, a complete
|
||||||
|
machine-readable copy of the corresponding source code, to be
|
||||||
|
distributed under the terms of Sections 1 and 2 above on a medium
|
||||||
|
customarily used for software interchange; or,
|
||||||
|
|
||||||
|
c) Accompany it with the information you received as to the offer
|
||||||
|
to distribute corresponding source code. (This alternative is
|
||||||
|
allowed only for noncommercial distribution and only if you
|
||||||
|
received the program in object code or executable form with such
|
||||||
|
an offer, in accord with Subsection b above.)
|
||||||
|
|
||||||
|
The source code for a work means the preferred form of the work for
|
||||||
|
making modifications to it. For an executable work, complete source
|
||||||
|
code means all the source code for all modules it contains, plus any
|
||||||
|
associated interface definition files, plus the scripts used to
|
||||||
|
control compilation and installation of the executable. However, as a
|
||||||
|
special exception, the source code distributed need not include
|
||||||
|
anything that is normally distributed (in either source or binary
|
||||||
|
form) with the major components (compiler, kernel, and so on) of the
|
||||||
|
operating system on which the executable runs, unless that component
|
||||||
|
itself accompanies the executable.
|
||||||
|
|
||||||
|
If distribution of executable or object code is made by offering
|
||||||
|
access to copy from a designated place, then offering equivalent
|
||||||
|
access to copy the source code from the same place counts as
|
||||||
|
distribution of the source code, even though third parties are not
|
||||||
|
compelled to copy the source along with the object code.
|
||||||
|
|
||||||
|
4. You may not copy, modify, sublicense, or distribute the Program
|
||||||
|
except as expressly provided under this License. Any attempt
|
||||||
|
otherwise to copy, modify, sublicense or distribute the Program is
|
||||||
|
void, and will automatically terminate your rights under this License.
|
||||||
|
However, parties who have received copies, or rights, from you under
|
||||||
|
this License will not have their licenses terminated so long as such
|
||||||
|
parties remain in full compliance.
|
||||||
|
|
||||||
|
5. You are not required to accept this License, since you have not
|
||||||
|
signed it. However, nothing else grants you permission to modify or
|
||||||
|
distribute the Program or its derivative works. These actions are
|
||||||
|
prohibited by law if you do not accept this License. Therefore, by
|
||||||
|
modifying or distributing the Program (or any work based on the
|
||||||
|
Program), you indicate your acceptance of this License to do so, and
|
||||||
|
all its terms and conditions for copying, distributing or modifying
|
||||||
|
the Program or works based on it.
|
||||||
|
|
||||||
|
6. Each time you redistribute the Program (or any work based on the
|
||||||
|
Program), the recipient automatically receives a license from the
|
||||||
|
original licensor to copy, distribute or modify the Program subject to
|
||||||
|
these terms and conditions. You may not impose any further
|
||||||
|
restrictions on the recipients' exercise of the rights granted herein.
|
||||||
|
You are not responsible for enforcing compliance by third parties to
|
||||||
|
this License.
|
||||||
|
|
||||||
|
7. If, as a consequence of a court judgment or allegation of patent
|
||||||
|
infringement or for any other reason (not limited to patent issues),
|
||||||
|
conditions are imposed on you (whether by court order, agreement or
|
||||||
|
otherwise) that contradict the conditions of this License, they do not
|
||||||
|
excuse you from the conditions of this License. If you cannot
|
||||||
|
distribute so as to satisfy simultaneously your obligations under this
|
||||||
|
License and any other pertinent obligations, then as a consequence you
|
||||||
|
may not distribute the Program at all. For example, if a patent
|
||||||
|
license would not permit royalty-free redistribution of the Program by
|
||||||
|
all those who receive copies directly or indirectly through you, then
|
||||||
|
the only way you could satisfy both it and this License would be to
|
||||||
|
refrain entirely from distribution of the Program.
|
||||||
|
|
||||||
|
If any portion of this section is held invalid or unenforceable under
|
||||||
|
any particular circumstance, the balance of the section is intended to
|
||||||
|
apply and the section as a whole is intended to apply in other
|
||||||
|
circumstances.
|
||||||
|
|
||||||
|
It is not the purpose of this section to induce you to infringe any
|
||||||
|
patents or other property right claims or to contest validity of any
|
||||||
|
such claims; this section has the sole purpose of protecting the
|
||||||
|
integrity of the free software distribution system, which is
|
||||||
|
implemented by public license practices. Many people have made
|
||||||
|
generous contributions to the wide range of software distributed
|
||||||
|
through that system in reliance on consistent application of that
|
||||||
|
system; it is up to the author/donor to decide if he or she is willing
|
||||||
|
to distribute software through any other system and a licensee cannot
|
||||||
|
impose that choice.
|
||||||
|
|
||||||
|
This section is intended to make thoroughly clear what is believed to
|
||||||
|
be a consequence of the rest of this License.
|
||||||
|
|
||||||
|
8. If the distribution and/or use of the Program is restricted in
|
||||||
|
certain countries either by patents or by copyrighted interfaces, the
|
||||||
|
original copyright holder who places the Program under this License
|
||||||
|
may add an explicit geographical distribution limitation excluding
|
||||||
|
those countries, so that distribution is permitted only in or among
|
||||||
|
countries not thus excluded. In such case, this License incorporates
|
||||||
|
the limitation as if written in the body of this License.
|
||||||
|
|
||||||
|
9. The Free Software Foundation may publish revised and/or new versions
|
||||||
|
of the General Public License from time to time. Such new versions will
|
||||||
|
be similar in spirit to the present version, but may differ in detail to
|
||||||
|
address new problems or concerns.
|
||||||
|
|
||||||
|
Each version is given a distinguishing version number. If the Program
|
||||||
|
specifies a version number of this License which applies to it and "any
|
||||||
|
later version", you have the option of following the terms and conditions
|
||||||
|
either of that version or of any later version published by the Free
|
||||||
|
Software Foundation. If the Program does not specify a version number of
|
||||||
|
this License, you may choose any version ever published by the Free Software
|
||||||
|
Foundation.
|
||||||
|
|
||||||
|
10. If you wish to incorporate parts of the Program into other free
|
||||||
|
programs whose distribution conditions are different, write to the author
|
||||||
|
to ask for permission. For software which is copyrighted by the Free
|
||||||
|
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||||
|
make exceptions for this. Our decision will be guided by the two goals
|
||||||
|
of preserving the free status of all derivatives of our free software and
|
||||||
|
of promoting the sharing and reuse of software generally.
|
||||||
|
|
||||||
|
NO WARRANTY
|
||||||
|
|
||||||
|
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||||
|
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||||
|
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||||
|
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||||
|
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||||
|
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||||
|
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||||
|
REPAIR OR CORRECTION.
|
||||||
|
|
||||||
|
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||||
|
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||||
|
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||||
|
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||||
|
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||||
|
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||||
|
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||||
|
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||||
|
POSSIBILITY OF SUCH DAMAGES.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
How to Apply These Terms to Your New Programs
|
||||||
|
|
||||||
|
If you develop a new program, and you want it to be of the greatest
|
||||||
|
possible use to the public, the best way to achieve this is to make it
|
||||||
|
free software which everyone can redistribute and change under these terms.
|
||||||
|
|
||||||
|
To do so, attach the following notices to the program. It is safest
|
||||||
|
to attach them to the start of each source file to most effectively
|
||||||
|
convey the exclusion of warranty; and each file should have at least
|
||||||
|
the "copyright" line and a pointer to where the full notice is found.
|
||||||
|
|
||||||
|
<one line to give the program's name and a brief idea of what it does.>
|
||||||
|
Copyright (C) <year> <name of author>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with this program; if not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
|
If the program is interactive, make it output a short notice like this
|
||||||
|
when it starts in an interactive mode:
|
||||||
|
|
||||||
|
Gnomovision version 69, Copyright (C) year name of author
|
||||||
|
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||||
|
This is free software, and you are welcome to redistribute it
|
||||||
|
under certain conditions; type `show c' for details.
|
||||||
|
|
||||||
|
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||||
|
parts of the General Public License. Of course, the commands you use may
|
||||||
|
be called something other than `show w' and `show c'; they could even be
|
||||||
|
mouse-clicks or menu items--whatever suits your program.
|
||||||
|
|
||||||
|
You should also get your employer (if you work as a programmer) or your
|
||||||
|
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||||
|
necessary. Here is a sample; alter the names:
|
||||||
|
|
||||||
|
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||||
|
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||||
|
|
||||||
|
<signature of Moe Ghoul>, 1 April 1989
|
||||||
|
Moe Ghoul, President of Vice
|
||||||
|
|
||||||
|
This General Public License does not permit incorporating your program into
|
||||||
|
proprietary programs. If your program is a subroutine library, you may
|
||||||
|
consider it more useful to permit linking proprietary applications with the
|
||||||
|
library. If this is what you want to do, use the GNU Lesser General
|
||||||
|
Public License instead of this License.
|
||||||
6
README.md
Normal file
6
README.md
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
# MiniRoute
|
||||||
|
|
||||||
|
MiniRoute is a learning project, implementing a router in C.
|
||||||
|
|
||||||
|
License: GPLv2-only
|
||||||
|
Copyright: 2026, by DcruBro (Jonas Korene Novak)
|
||||||
63
include/dynarr.h
Normal file
63
include/dynarr.h
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
#ifndef DYNARR_H
|
||||||
|
#define DYNARR_H
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#define DYNARR_MAX_CAPACITY ((size_t)0x7FFFFFFF)
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
size_t size;
|
||||||
|
size_t elemSize;
|
||||||
|
size_t capacity;
|
||||||
|
void* data;
|
||||||
|
} DynArr;
|
||||||
|
|
||||||
|
// Do not use; Use DYNARR_CREATE macro instead.
|
||||||
|
DynArr* DynArr_create(size_t elemSize, size_t capacity);
|
||||||
|
|
||||||
|
// Reserve n blocks in arary; New size will be n, NOT size + n; Reserving less memory that current will fail, use prune instead.
|
||||||
|
void DynArr_reserve(DynArr* p, size_t n);
|
||||||
|
|
||||||
|
// Push data into a new block at the end of the array
|
||||||
|
void* DynArr_push_back(DynArr* p, void* value);
|
||||||
|
|
||||||
|
// Remove the last block in the array.
|
||||||
|
void DynArr_pop_back(DynArr* p);
|
||||||
|
|
||||||
|
// Remove first block from array.
|
||||||
|
void DynArr_pop_front(DynArr* p);
|
||||||
|
|
||||||
|
// Remove index from array. This moves all blocks after the index block.
|
||||||
|
void DynArr_remove(DynArr* p, size_t index);
|
||||||
|
|
||||||
|
// Erase the array. This will not free unused blocks.
|
||||||
|
void DynArr_erase(DynArr* p);
|
||||||
|
|
||||||
|
// Prune and free unused blocks. If pruning to zero, ensure to reserve after.
|
||||||
|
void DynArr_prune(DynArr* p);
|
||||||
|
|
||||||
|
// Get a pointer to a block by index
|
||||||
|
void* DynArr_at(DynArr* p, size_t index);
|
||||||
|
|
||||||
|
// Get the index by block pointer
|
||||||
|
size_t DynArr_at_ptr(DynArr* p, void* ptr);
|
||||||
|
|
||||||
|
// Get size
|
||||||
|
size_t DynArr_size(DynArr* p);
|
||||||
|
|
||||||
|
// Get element size
|
||||||
|
size_t DynArr_elemSize(DynArr* p);
|
||||||
|
|
||||||
|
// Get capacity
|
||||||
|
size_t DynArr_capacity(DynArr* p);
|
||||||
|
|
||||||
|
void DynArr_destroy(DynArr* p);
|
||||||
|
|
||||||
|
// Note: Make sure to not overread or overwrite
|
||||||
|
void* DynArr_c_arr(DynArr* p);
|
||||||
|
|
||||||
|
#define DYNARR_CREATE(T, initialCapacity) DynArr_create(sizeof(T), initialCapacity)
|
||||||
|
|
||||||
|
#endif
|
||||||
20
include/dynset.h
Normal file
20
include/dynset.h
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
#ifndef DYNSET_H
|
||||||
|
#define DYNSET_H
|
||||||
|
|
||||||
|
#include <dynarr.h>
|
||||||
|
|
||||||
|
// Dynamic Set structure - basically DynArr with uniqueness enforced
|
||||||
|
typedef struct {
|
||||||
|
DynArr* arr;
|
||||||
|
} DynSet;
|
||||||
|
|
||||||
|
// Function prototypes
|
||||||
|
DynSet* DynSet_Create(size_t elemSize);
|
||||||
|
void DynSet_Destroy(DynSet* set);
|
||||||
|
int DynSet_Insert(DynSet* set, const void* element);
|
||||||
|
int DynSet_Contains(DynSet* set, const void* element);
|
||||||
|
size_t DynSet_Size(DynSet* set);
|
||||||
|
void* DynSet_Get(DynSet* set, size_t index);
|
||||||
|
void DynSet_Remove(DynSet* set, const void* element);
|
||||||
|
|
||||||
|
#endif
|
||||||
42
include/forward/if/interface.h
Normal file
42
include/forward/if/interface.h
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
#ifndef INTERFACE_H
|
||||||
|
#define INTERFACE_H
|
||||||
|
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
typedef struct interface_v4 interface_v4_t;
|
||||||
|
typedef struct interface_v6 interface_v6_t;
|
||||||
|
|
||||||
|
typedef struct interface_packet {
|
||||||
|
sa_family_t family;
|
||||||
|
const uint8_t* data;
|
||||||
|
size_t length;
|
||||||
|
struct sockaddr_storage source;
|
||||||
|
struct sockaddr_storage destination;
|
||||||
|
} interface_packet_t;
|
||||||
|
|
||||||
|
typedef void (*interface_packet_callback_t)(const interface_packet_t* packet, void* userData);
|
||||||
|
|
||||||
|
int Interface_Init(void);
|
||||||
|
void Interface_Cleanup(void);
|
||||||
|
|
||||||
|
interface_v4_t* Interface_Create_v4(const char* localAddress, interface_packet_callback_t callback, void* userData);
|
||||||
|
interface_v6_t* Interface_Create_v6(const char* localAddress, interface_packet_callback_t callback, void* userData);
|
||||||
|
|
||||||
|
int Interface_Listen_v4(interface_v4_t* iface);
|
||||||
|
int Interface_Listen_v6(interface_v6_t* iface);
|
||||||
|
|
||||||
|
void Interface_Stop_v4(interface_v4_t* iface);
|
||||||
|
void Interface_Stop_v6(interface_v6_t* iface);
|
||||||
|
|
||||||
|
void Interface_Destroy_v4(interface_v4_t* iface);
|
||||||
|
void Interface_Destroy_v6(interface_v6_t* iface);
|
||||||
|
|
||||||
|
int Interface_SendRaw_v4(interface_v4_t* iface, const struct in_addr* destination, const void* packet, size_t packetLength);
|
||||||
|
int Interface_SendRaw_v6(interface_v6_t* iface, const struct in6_addr* destination, const void* packet, size_t packetLength);
|
||||||
|
|
||||||
|
int Interface_SendFrame_v4(interface_v4_t* iface, const void* frame, size_t frameLength);
|
||||||
|
int Interface_SendFrame_v6(interface_v6_t* iface, const void* frame, size_t frameLength);
|
||||||
|
|
||||||
|
#endif
|
||||||
30
include/forward/routetable.h
Normal file
30
include/forward/routetable.h
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
#ifndef ROUTETABLE_H
|
||||||
|
#define ROUTETABLE_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <libart/art.h>
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint32_t destination;
|
||||||
|
uint32_t mask;
|
||||||
|
uint32_t nextHop;
|
||||||
|
} route_table_v4_entry_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
__uint128_t destination;
|
||||||
|
__uint128_t mask;
|
||||||
|
__uint128_t nextHop;
|
||||||
|
} route_table_v6_entry_t;
|
||||||
|
|
||||||
|
// General rule: 0 = success, -1 = failure (Note: per-function return values may vary, will be documented in the .c file)
|
||||||
|
int RouteTable_Init(void);
|
||||||
|
void RouteTable_Cleanup(void);
|
||||||
|
|
||||||
|
int RouteTable_AddRoute_v4(uint32_t destination, uint32_t mask, uint32_t nextHop);
|
||||||
|
int RouteTable_AddRoute_v6(__uint128_t destination, __uint128_t mask, __uint128_t nextHop);
|
||||||
|
|
||||||
|
// GetNextHop performs longest prefix match; writes best matching next hop into *nextHop
|
||||||
|
int RouteTable_GetNextHop_v4(uint32_t destination, uint32_t* nextHop);
|
||||||
|
int RouteTable_GetNextHop_v6(__uint128_t destination, __uint128_t* nextHop);
|
||||||
|
|
||||||
|
#endif
|
||||||
21
include/iputils.h
Normal file
21
include/iputils.h
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
#ifndef IPUTILS_H
|
||||||
|
#define IPUTILS_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
uint32_t IPUtils_CIDRToMaskv4(uint8_t cidr);
|
||||||
|
uint8_t IPUtils_MaskToCIDRv4(uint32_t mask);
|
||||||
|
bool IPUtils_MatchPrefixv4(uint32_t ip, uint32_t prefix, uint8_t cidr);
|
||||||
|
// If buf is NULL, -> stdout; if NOT NULL, -> buf (must be at least 16 bytes)
|
||||||
|
void IPUtils_PrintIPv4(uint32_t ip, char* buf);
|
||||||
|
|
||||||
|
__uint128_t IPUtils_CIDRToMaskv6(uint8_t cidr);
|
||||||
|
uint8_t IPUtils_MaskToCIDRv6(__uint128_t mask);
|
||||||
|
bool IPUtils_MatchPrefixv6(__uint128_t ip, __uint128_t prefix, uint8_t cidr);
|
||||||
|
// If buf is NULL, -> stdout; if NOT NULL, -> buf (must be at least 40 bytes)
|
||||||
|
void IPUtils_PrintIPv6(__uint128_t ip, char* buf);
|
||||||
|
|
||||||
|
#endif
|
||||||
215
include/libart/art.h
Normal file
215
include/libart/art.h
Normal file
@@ -0,0 +1,215 @@
|
|||||||
|
#include <stdint.h>
|
||||||
|
#ifndef ART_H
|
||||||
|
#define ART_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define NODE4 1
|
||||||
|
#define NODE16 2
|
||||||
|
#define NODE48 3
|
||||||
|
#define NODE256 4
|
||||||
|
|
||||||
|
#define MAX_PREFIX_LEN 10
|
||||||
|
|
||||||
|
#if defined(__GNUC__) && !defined(__clang__)
|
||||||
|
# if __STDC_VERSION__ >= 199901L && 402 == (__GNUC__ * 100 + __GNUC_MINOR__)
|
||||||
|
/*
|
||||||
|
* GCC 4.2.2's C99 inline keyword support is pretty broken; avoid. Introduced in
|
||||||
|
* GCC 4.2.something, fixed in 4.3.0. So checking for specific major.minor of
|
||||||
|
* 4.2 is fine.
|
||||||
|
*/
|
||||||
|
# define BROKEN_GCC_C99_INLINE
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef int(*art_callback)(void *data, const unsigned char *key, uint32_t key_len, void *value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This struct is included as part
|
||||||
|
* of all the various node sizes
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
uint32_t partial_len;
|
||||||
|
uint8_t type;
|
||||||
|
uint8_t num_children;
|
||||||
|
unsigned char partial[MAX_PREFIX_LEN];
|
||||||
|
} art_node;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Small node with only 4 children
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
art_node n;
|
||||||
|
unsigned char keys[4];
|
||||||
|
art_node *children[4];
|
||||||
|
} art_node4;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Node with 16 children
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
art_node n;
|
||||||
|
unsigned char keys[16];
|
||||||
|
art_node *children[16];
|
||||||
|
} art_node16;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Node with 48 children, but
|
||||||
|
* a full 256 byte field.
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
art_node n;
|
||||||
|
unsigned char keys[256];
|
||||||
|
art_node *children[48];
|
||||||
|
} art_node48;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Full node with 256 children
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
art_node n;
|
||||||
|
art_node *children[256];
|
||||||
|
} art_node256;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a leaf. These are
|
||||||
|
* of arbitrary size, as they include the key.
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
void *value;
|
||||||
|
uint32_t key_len;
|
||||||
|
unsigned char key[];
|
||||||
|
} art_leaf;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main struct, points to root.
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
art_node *root;
|
||||||
|
uint64_t size;
|
||||||
|
} art_tree;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes an ART tree
|
||||||
|
* @return 0 on success.
|
||||||
|
*/
|
||||||
|
int art_tree_init(art_tree *t);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DEPRECATED
|
||||||
|
* Initializes an ART tree
|
||||||
|
* @return 0 on success.
|
||||||
|
*/
|
||||||
|
#define init_art_tree(...) art_tree_init(__VA_ARGS__)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destroys an ART tree
|
||||||
|
* @return 0 on success.
|
||||||
|
*/
|
||||||
|
int art_tree_destroy(art_tree *t);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DEPRECATED
|
||||||
|
* Initializes an ART tree
|
||||||
|
* @return 0 on success.
|
||||||
|
*/
|
||||||
|
#define destroy_art_tree(...) art_tree_destroy(__VA_ARGS__)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the size of the ART tree.
|
||||||
|
*/
|
||||||
|
#ifdef BROKEN_GCC_C99_INLINE
|
||||||
|
# define art_size(t) ((t)->size)
|
||||||
|
#else
|
||||||
|
inline uint64_t art_size(art_tree *t) {
|
||||||
|
return t->size;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* inserts a new value into the art tree
|
||||||
|
* @arg t the tree
|
||||||
|
* @arg key the key
|
||||||
|
* @arg key_len the length of the key
|
||||||
|
* @arg value opaque value.
|
||||||
|
* @return null if the item was newly inserted, otherwise
|
||||||
|
* the old value pointer is returned.
|
||||||
|
*/
|
||||||
|
void* art_insert(art_tree *t, const unsigned char *key, int key_len, void *value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* inserts a new value into the art tree (not replacing)
|
||||||
|
* @arg t the tree
|
||||||
|
* @arg key the key
|
||||||
|
* @arg key_len the length of the key
|
||||||
|
* @arg value opaque value.
|
||||||
|
* @return null if the item was newly inserted, otherwise
|
||||||
|
* the old value pointer is returned.
|
||||||
|
*/
|
||||||
|
void* art_insert_no_replace(art_tree *t, const unsigned char *key, int key_len, void *value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes a value from the ART tree
|
||||||
|
* @arg t The tree
|
||||||
|
* @arg key The key
|
||||||
|
* @arg key_len The length of the key
|
||||||
|
* @return NULL if the item was not found, otherwise
|
||||||
|
* the value pointer is returned.
|
||||||
|
*/
|
||||||
|
void* art_delete(art_tree *t, const unsigned char *key, int key_len);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Searches for a value in the ART tree
|
||||||
|
* @arg t The tree
|
||||||
|
* @arg key The key
|
||||||
|
* @arg key_len The length of the key
|
||||||
|
* @return NULL if the item was not found, otherwise
|
||||||
|
* the value pointer is returned.
|
||||||
|
*/
|
||||||
|
void* art_search(const art_tree *t, const unsigned char *key, int key_len);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the minimum valued leaf
|
||||||
|
* @return The minimum leaf or NULL
|
||||||
|
*/
|
||||||
|
art_leaf* art_minimum(art_tree *t);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the maximum valued leaf
|
||||||
|
* @return The maximum leaf or NULL
|
||||||
|
*/
|
||||||
|
art_leaf* art_maximum(art_tree *t);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Iterates through the entries pairs in the map,
|
||||||
|
* invoking a callback for each. The call back gets a
|
||||||
|
* key, value for each and returns an integer stop value.
|
||||||
|
* If the callback returns non-zero, then the iteration stops.
|
||||||
|
* @arg t The tree to iterate over
|
||||||
|
* @arg cb The callback function to invoke
|
||||||
|
* @arg data Opaque handle passed to the callback
|
||||||
|
* @return 0 on success, or the return of the callback.
|
||||||
|
*/
|
||||||
|
int art_iter(art_tree *t, art_callback cb, void *data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Iterates through the entries pairs in the map,
|
||||||
|
* invoking a callback for each that matches a given prefix.
|
||||||
|
* The call back gets a key, value for each and returns an integer stop value.
|
||||||
|
* If the callback returns non-zero, then the iteration stops.
|
||||||
|
* @arg t The tree to iterate over
|
||||||
|
* @arg prefix The prefix of keys to read
|
||||||
|
* @arg prefix_len The length of the prefix
|
||||||
|
* @arg cb The callback function to invoke
|
||||||
|
* @arg data Opaque handle passed to the callback
|
||||||
|
* @return 0 on success, or the return of the callback.
|
||||||
|
*/
|
||||||
|
int art_iter_prefix(art_tree *t, const unsigned char *prefix, int prefix_len, art_callback cb, void *data);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
179
src/dynarr.c
Normal file
179
src/dynarr.c
Normal file
@@ -0,0 +1,179 @@
|
|||||||
|
#include <dynarr.h>
|
||||||
|
|
||||||
|
DynArr* DynArr_create(size_t elemSize, size_t capacity) {
|
||||||
|
DynArr* p = (DynArr*)malloc(sizeof(DynArr));
|
||||||
|
if (!p) return NULL;
|
||||||
|
|
||||||
|
p->elemSize = elemSize;
|
||||||
|
p->capacity = capacity;
|
||||||
|
p->data = malloc(elemSize * capacity);
|
||||||
|
if (!p->data) {
|
||||||
|
free(p);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
p->size = 0;
|
||||||
|
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reserve n blocks in arary; New size will be n, NOT size + n; Reserving less memory that current will fail, use prune instead.
|
||||||
|
void DynArr_reserve(DynArr* p, size_t n) {
|
||||||
|
if (n <= p->capacity) {
|
||||||
|
printf("reserve ignored; attempted to reserve less or equal to current capacity\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (n > DYNARR_MAX_CAPACITY) {
|
||||||
|
printf("reserve ignored; attempted to reserve over 32 bits\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* new_data = realloc(p->data, n * p->elemSize);
|
||||||
|
if (!new_data) {
|
||||||
|
printf("reserve failed\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
p->data = new_data;
|
||||||
|
p->capacity = n;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Push data into a new block at the end of the array; If value is NULL, the new block will be zeroed.
|
||||||
|
void* DynArr_push_back(DynArr* p, void* value) {
|
||||||
|
//if (value == NULL) {
|
||||||
|
// printf("push_back ignored; value is null");
|
||||||
|
// return NULL;
|
||||||
|
//}
|
||||||
|
|
||||||
|
if (p->size >= p->capacity) {
|
||||||
|
size_t new_cap = (p->capacity == 0) ? 1 : p->capacity * 2;
|
||||||
|
|
||||||
|
if (new_cap < p->capacity || new_cap > DYNARR_MAX_CAPACITY) {
|
||||||
|
printf("push_back ignored; capacity overflow\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* new_data = realloc(p->data, new_cap * p->elemSize);
|
||||||
|
if (!new_data) {
|
||||||
|
printf("push failed\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
p->capacity = new_cap;
|
||||||
|
p->data = new_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* dst = (void*)((char*)p->data + (p->size * p->elemSize));
|
||||||
|
|
||||||
|
if (value == NULL) {
|
||||||
|
memset(dst, 0, p->elemSize); // Handle NULL value.
|
||||||
|
} else {
|
||||||
|
memcpy((char*)dst, value, p->elemSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
p->size++;
|
||||||
|
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the last block in the array.
|
||||||
|
void DynArr_pop_back(DynArr* p) {
|
||||||
|
if (p->size == 0) {
|
||||||
|
printf("pop_back ignored; size is 0\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
p->size--; // Will automatically overwrite that memory naturally
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove first block from array.
|
||||||
|
void DynArr_pop_front(DynArr* p) {
|
||||||
|
if (p->size == 0) {
|
||||||
|
printf("pop_front ignored; size is 0\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
memmove(
|
||||||
|
(char*)p->data,
|
||||||
|
(char*)p->data + p->elemSize,
|
||||||
|
(p->size - 1) * p->elemSize
|
||||||
|
);
|
||||||
|
|
||||||
|
p->size--;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove index from array. This moves all blocks after the index block.
|
||||||
|
void DynArr_remove(DynArr* p, size_t index) {
|
||||||
|
if (index >= p->size) return;
|
||||||
|
|
||||||
|
memmove(
|
||||||
|
(char*)p->data + (index * p->elemSize),
|
||||||
|
(char*)p->data + (index + 1) * p->elemSize,
|
||||||
|
(p->size - index - 1) * p->elemSize
|
||||||
|
);
|
||||||
|
|
||||||
|
p->size--;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Erase the array. This will not free unused blocks.
|
||||||
|
void DynArr_erase(DynArr* p) {
|
||||||
|
p->size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prune and free unused blocks. If pruning to zero, ensure to reserve after.
|
||||||
|
void DynArr_prune(DynArr* p) {
|
||||||
|
void* new_data = realloc(p->data, (p->size == 0 ? 1 : p->size) * p->elemSize);
|
||||||
|
if (!new_data) {
|
||||||
|
printf("pruning failed\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
p->data = new_data;
|
||||||
|
p->capacity = p->size;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get a pointer to a block by index
|
||||||
|
void* DynArr_at(DynArr* p, size_t index) {
|
||||||
|
if (index >= p->size) return NULL;
|
||||||
|
return (char*)p->data + (index * p->elemSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the index by block pointer
|
||||||
|
size_t DynArr_at_ptr(DynArr* p, void* ptr) {
|
||||||
|
if (!p || !ptr) {
|
||||||
|
printf("invalid pointer\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < p->size; i++) {
|
||||||
|
if ((void*)(((char*)p->data) + (i * p->elemSize)) == ptr) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If for some reason the array has 2^64 elements in it, then fuck it, I guess we'll just crash, I don't care.
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get size
|
||||||
|
size_t DynArr_size(DynArr* p) {
|
||||||
|
return p->size;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get element size
|
||||||
|
size_t DynArr_elemSize(DynArr* p) {
|
||||||
|
return p->elemSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get capacity
|
||||||
|
size_t DynArr_capacity(DynArr* p) {
|
||||||
|
return p->capacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DynArr_destroy(DynArr* p) {
|
||||||
|
if (!p) return;
|
||||||
|
free(p->data);
|
||||||
|
free(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
void* DynArr_c_arr(DynArr* p) {
|
||||||
|
return p->data;
|
||||||
|
}
|
||||||
58
src/dynset.c
Normal file
58
src/dynset.c
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
#include <dynset.h>
|
||||||
|
|
||||||
|
DynSet* DynSet_Create(size_t elemSize) {
|
||||||
|
DynSet* set = (DynSet*)malloc(sizeof(DynSet));
|
||||||
|
if (!set) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
set->arr = DynArr_create(elemSize, 1);
|
||||||
|
if (!set->arr) {
|
||||||
|
free(set);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return set;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DynSet_Destroy(DynSet* set) {
|
||||||
|
if (set) {
|
||||||
|
DynArr_destroy(set->arr);
|
||||||
|
free(set);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int DynSet_Insert(DynSet* set, const void* element) {
|
||||||
|
if (DynSet_Contains(set, element)) {
|
||||||
|
return 0; // Element already exists
|
||||||
|
}
|
||||||
|
return DynArr_push_back(set->arr, element) != NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int DynSet_Contains(DynSet* set, const void* element) {
|
||||||
|
size_t size = DynArr_size(set->arr);
|
||||||
|
for (size_t i = 0; i < size; i++) {
|
||||||
|
void* current = DynArr_at(set->arr, i);
|
||||||
|
if (memcmp(current, element, set->arr->elemSize) == 0) {
|
||||||
|
return 1; // Found
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0; // Not found
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t DynSet_Size(DynSet* set) {
|
||||||
|
return DynArr_size(set->arr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void* DynSet_Get(DynSet* set, size_t index) {
|
||||||
|
return DynArr_at(set->arr, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DynSet_Remove(DynSet* set, const void* element) {
|
||||||
|
size_t size = DynArr_size(set->arr);
|
||||||
|
for (size_t i = 0; i < size; i++) {
|
||||||
|
void* current = DynArr_at(set->arr, i);
|
||||||
|
if (memcmp(current, element, set->arr->elemSize) == 0) {
|
||||||
|
DynArr_remove(set->arr, i);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
413
src/forward/if/interface.c
Normal file
413
src/forward/if/interface.c
Normal file
@@ -0,0 +1,413 @@
|
|||||||
|
#include <forward/if/interface.h>
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#ifndef IPV6_HDRINCL
|
||||||
|
#define IPV6_HDRINCL 36
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct interface_v4 {
|
||||||
|
int listenFd;
|
||||||
|
int sendFd;
|
||||||
|
int listening;
|
||||||
|
struct sockaddr_in localAddress;
|
||||||
|
pthread_t thread;
|
||||||
|
interface_packet_callback_t callback;
|
||||||
|
void* userData;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct interface_v6 {
|
||||||
|
int listenFd;
|
||||||
|
int sendFd;
|
||||||
|
int listening;
|
||||||
|
struct sockaddr_in6 localAddress;
|
||||||
|
pthread_t thread;
|
||||||
|
interface_packet_callback_t callback;
|
||||||
|
void* userData;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int interface_parse_ipv4(const char* localAddress, struct in_addr* outAddress) {
|
||||||
|
if (!outAddress) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (localAddress == NULL || localAddress[0] == '\0') {
|
||||||
|
outAddress->s_addr = htonl(INADDR_ANY);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return inet_pton(AF_INET, localAddress, outAddress) == 1 ? 0 : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int interface_parse_ipv6(const char* localAddress, struct in6_addr* outAddress) {
|
||||||
|
if (!outAddress) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (localAddress == NULL || localAddress[0] == '\0') {
|
||||||
|
*outAddress = in6addr_any;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return inet_pton(AF_INET6, localAddress, outAddress) == 1 ? 0 : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int interface_open_v4(interface_v4_t* iface) {
|
||||||
|
iface->listenFd = socket(AF_INET, SOCK_RAW, IPPROTO_IP);
|
||||||
|
if (iface->listenFd < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
iface->sendFd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
|
||||||
|
if (iface->sendFd < 0) {
|
||||||
|
close(iface->listenFd);
|
||||||
|
iface->listenFd = -1;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
int one = 1;
|
||||||
|
if (setsockopt(iface->sendFd, IPPROTO_IP, IP_HDRINCL, &one, sizeof(one)) < 0) {
|
||||||
|
close(iface->listenFd);
|
||||||
|
close(iface->sendFd);
|
||||||
|
iface->listenFd = -1;
|
||||||
|
iface->sendFd = -1;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bind(iface->listenFd, (struct sockaddr*)&iface->localAddress, sizeof(iface->localAddress)) < 0) {
|
||||||
|
close(iface->listenFd);
|
||||||
|
close(iface->sendFd);
|
||||||
|
iface->listenFd = -1;
|
||||||
|
iface->sendFd = -1;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int interface_open_v6(interface_v6_t* iface) {
|
||||||
|
iface->listenFd = socket(AF_INET6, SOCK_RAW, IPPROTO_IPV6);
|
||||||
|
if (iface->listenFd < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
iface->sendFd = socket(AF_INET6, SOCK_RAW, IPPROTO_RAW);
|
||||||
|
if (iface->sendFd < 0) {
|
||||||
|
close(iface->listenFd);
|
||||||
|
iface->listenFd = -1;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
int one = 1;
|
||||||
|
#ifdef IPV6_HDRINCL
|
||||||
|
if (setsockopt(iface->sendFd, IPPROTO_IPV6, IPV6_HDRINCL, &one, sizeof(one)) < 0) {
|
||||||
|
close(iface->listenFd);
|
||||||
|
close(iface->sendFd);
|
||||||
|
iface->listenFd = -1;
|
||||||
|
iface->sendFd = -1;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bind(iface->listenFd, (struct sockaddr*)&iface->localAddress, sizeof(iface->localAddress)) < 0) {
|
||||||
|
close(iface->listenFd);
|
||||||
|
close(iface->sendFd);
|
||||||
|
iface->listenFd = -1;
|
||||||
|
iface->sendFd = -1;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void interface_fill_packet_common(interface_packet_t* packet, sa_family_t family, const void* data, size_t length, const struct sockaddr* source, const struct sockaddr* destination) {
|
||||||
|
size_t sourceSize = 0;
|
||||||
|
size_t destinationSize = 0;
|
||||||
|
|
||||||
|
packet->family = family;
|
||||||
|
packet->data = (const uint8_t*)data;
|
||||||
|
packet->length = length;
|
||||||
|
memset(&packet->source, 0, sizeof(packet->source));
|
||||||
|
memset(&packet->destination, 0, sizeof(packet->destination));
|
||||||
|
|
||||||
|
if (source) {
|
||||||
|
sourceSize = (source->sa_family == AF_INET6) ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in);
|
||||||
|
memcpy(&packet->source, source, sourceSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (destination) {
|
||||||
|
destinationSize = (destination->sa_family == AF_INET6) ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in);
|
||||||
|
memcpy(&packet->destination, destination, destinationSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void* interface_listen_v4_thread(void* arg) {
|
||||||
|
interface_v4_t* iface = (interface_v4_t*)arg;
|
||||||
|
uint8_t buffer[65535];
|
||||||
|
|
||||||
|
while (iface->listening) {
|
||||||
|
struct sockaddr_storage source;
|
||||||
|
socklen_t sourceLength = sizeof(source);
|
||||||
|
ssize_t received = recvfrom(iface->listenFd, buffer, sizeof(buffer), 0, (struct sockaddr*)&source, &sourceLength);
|
||||||
|
|
||||||
|
if (received < 0) {
|
||||||
|
if (!iface->listening || errno == EBADF || errno == EINVAL) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (iface->callback) {
|
||||||
|
uint8_t* packetCopy = (uint8_t*)malloc((size_t)received);
|
||||||
|
if (!packetCopy) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface_packet_t packet;
|
||||||
|
|
||||||
|
memcpy(packetCopy, buffer, (size_t)received);
|
||||||
|
interface_fill_packet_common(&packet, AF_INET, packetCopy, (size_t)received, (struct sockaddr*)&source, (struct sockaddr*)&iface->localAddress);
|
||||||
|
iface->callback(&packet, iface->userData);
|
||||||
|
free(packetCopy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void* interface_listen_v6_thread(void* arg) {
|
||||||
|
interface_v6_t* iface = (interface_v6_t*)arg;
|
||||||
|
uint8_t buffer[65535];
|
||||||
|
|
||||||
|
while (iface->listening) {
|
||||||
|
struct sockaddr_storage source;
|
||||||
|
socklen_t sourceLength = sizeof(source);
|
||||||
|
ssize_t received = recvfrom(iface->listenFd, buffer, sizeof(buffer), 0, (struct sockaddr*)&source, &sourceLength);
|
||||||
|
|
||||||
|
if (received < 0) {
|
||||||
|
if (!iface->listening || errno == EBADF || errno == EINVAL) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (iface->callback) {
|
||||||
|
uint8_t* packetCopy = (uint8_t*)malloc((size_t)received);
|
||||||
|
if (!packetCopy) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface_packet_t packet;
|
||||||
|
|
||||||
|
memcpy(packetCopy, buffer, (size_t)received);
|
||||||
|
interface_fill_packet_common(&packet, AF_INET6, packetCopy, (size_t)received, (struct sockaddr*)&source, (struct sockaddr*)&iface->localAddress);
|
||||||
|
iface->callback(&packet, iface->userData);
|
||||||
|
free(packetCopy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Interface_Init(void) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Interface_Cleanup(void) {
|
||||||
|
}
|
||||||
|
|
||||||
|
interface_v4_t* Interface_Create_v4(const char* localAddress, interface_packet_callback_t callback, void* userData) {
|
||||||
|
interface_v4_t* iface = (interface_v4_t*)calloc(1, sizeof(*iface));
|
||||||
|
if (!iface) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
iface->listenFd = -1;
|
||||||
|
iface->sendFd = -1;
|
||||||
|
iface->callback = callback;
|
||||||
|
iface->userData = userData;
|
||||||
|
iface->localAddress.sin_family = AF_INET;
|
||||||
|
|
||||||
|
if (interface_parse_ipv4(localAddress, &iface->localAddress.sin_addr) < 0) {
|
||||||
|
free(iface);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return iface;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface_v6_t* Interface_Create_v6(const char* localAddress, interface_packet_callback_t callback, void* userData) {
|
||||||
|
interface_v6_t* iface = (interface_v6_t*)calloc(1, sizeof(*iface));
|
||||||
|
if (!iface) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
iface->listenFd = -1;
|
||||||
|
iface->sendFd = -1;
|
||||||
|
iface->callback = callback;
|
||||||
|
iface->userData = userData;
|
||||||
|
iface->localAddress.sin6_family = AF_INET6;
|
||||||
|
|
||||||
|
if (interface_parse_ipv6(localAddress, &iface->localAddress.sin6_addr) < 0) {
|
||||||
|
free(iface);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return iface;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Interface_Listen_v4(interface_v4_t* iface) {
|
||||||
|
if (!iface || iface->listening) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (interface_open_v4(iface) < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
iface->listening = 1;
|
||||||
|
if (pthread_create(&iface->thread, NULL, interface_listen_v4_thread, iface) != 0) {
|
||||||
|
iface->listening = 0;
|
||||||
|
close(iface->listenFd);
|
||||||
|
close(iface->sendFd);
|
||||||
|
iface->listenFd = -1;
|
||||||
|
iface->sendFd = -1;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Interface_Listen_v6(interface_v6_t* iface) {
|
||||||
|
if (!iface || iface->listening) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (interface_open_v6(iface) < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
iface->listening = 1;
|
||||||
|
if (pthread_create(&iface->thread, NULL, interface_listen_v6_thread, iface) != 0) {
|
||||||
|
iface->listening = 0;
|
||||||
|
close(iface->listenFd);
|
||||||
|
close(iface->sendFd);
|
||||||
|
iface->listenFd = -1;
|
||||||
|
iface->sendFd = -1;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Interface_Stop_v4(interface_v4_t* iface) {
|
||||||
|
if (!iface) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (iface->listening) {
|
||||||
|
iface->listening = 0;
|
||||||
|
if (iface->listenFd >= 0) {
|
||||||
|
close(iface->listenFd);
|
||||||
|
iface->listenFd = -1;
|
||||||
|
}
|
||||||
|
pthread_join(iface->thread, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (iface->sendFd >= 0) {
|
||||||
|
close(iface->sendFd);
|
||||||
|
iface->sendFd = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Interface_Stop_v6(interface_v6_t* iface) {
|
||||||
|
if (!iface) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (iface->listening) {
|
||||||
|
iface->listening = 0;
|
||||||
|
if (iface->listenFd >= 0) {
|
||||||
|
close(iface->listenFd);
|
||||||
|
iface->listenFd = -1;
|
||||||
|
}
|
||||||
|
pthread_join(iface->thread, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (iface->sendFd >= 0) {
|
||||||
|
close(iface->sendFd);
|
||||||
|
iface->sendFd = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Interface_Destroy_v4(interface_v4_t* iface) {
|
||||||
|
if (!iface) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Interface_Stop_v4(iface);
|
||||||
|
free(iface);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Interface_Destroy_v6(interface_v6_t* iface) {
|
||||||
|
if (!iface) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Interface_Stop_v6(iface);
|
||||||
|
free(iface);
|
||||||
|
}
|
||||||
|
|
||||||
|
int Interface_SendRaw_v4(interface_v4_t* iface, const struct in_addr* destination, const void* packet, size_t packetLength) {
|
||||||
|
struct sockaddr_in destinationAddress;
|
||||||
|
|
||||||
|
if (!iface || iface->sendFd < 0 || !destination || !packet || packetLength == 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(&destinationAddress, 0, sizeof(destinationAddress));
|
||||||
|
destinationAddress.sin_family = AF_INET;
|
||||||
|
destinationAddress.sin_addr = *destination;
|
||||||
|
|
||||||
|
return (sendto(iface->sendFd, packet, packetLength, 0, (struct sockaddr*)&destinationAddress, sizeof(destinationAddress)) < 0) ? -1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Interface_SendRaw_v6(interface_v6_t* iface, const struct in6_addr* destination, const void* packet, size_t packetLength) {
|
||||||
|
struct sockaddr_in6 destinationAddress;
|
||||||
|
|
||||||
|
if (!iface || iface->sendFd < 0 || !destination || !packet || packetLength == 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(&destinationAddress, 0, sizeof(destinationAddress));
|
||||||
|
destinationAddress.sin6_family = AF_INET6;
|
||||||
|
destinationAddress.sin6_addr = *destination;
|
||||||
|
|
||||||
|
return (sendto(iface->sendFd, packet, packetLength, 0, (struct sockaddr*)&destinationAddress, sizeof(destinationAddress)) < 0) ? -1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Interface_SendFrame_v4(interface_v4_t* iface, const void* frame, size_t frameLength) {
|
||||||
|
(void)iface;
|
||||||
|
(void)frame;
|
||||||
|
(void)frameLength;
|
||||||
|
errno = ENOTSUP;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Interface_SendFrame_v6(interface_v6_t* iface, const void* frame, size_t frameLength) {
|
||||||
|
(void)iface;
|
||||||
|
(void)frame;
|
||||||
|
(void)frameLength;
|
||||||
|
errno = ENOTSUP;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
125
src/forward/routetable.c
Normal file
125
src/forward/routetable.c
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
#include <forward/routetable.h>
|
||||||
|
#include <iputils.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
static art_tree route_table_v4;
|
||||||
|
static art_tree route_table_v6;
|
||||||
|
|
||||||
|
static void build_key_v4(unsigned char out[5], uint32_t network, uint8_t plen) {
|
||||||
|
uint32_t be = htonl(network);
|
||||||
|
memcpy(out, &be, 4);
|
||||||
|
out[4] = plen;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void build_key_v6(unsigned char out[17], __uint128_t network, uint8_t plen) {
|
||||||
|
uint32_t parts[4] = {
|
||||||
|
htonl((uint32_t)(network >> 96)),
|
||||||
|
htonl((uint32_t)(network >> 64)),
|
||||||
|
htonl((uint32_t)(network >> 32)),
|
||||||
|
htonl((uint32_t)(network))
|
||||||
|
};
|
||||||
|
memcpy(out, parts, 16);
|
||||||
|
out[16] = plen;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int free_value_cb(void *data, const unsigned char *key, uint32_t key_len, void *value) {
|
||||||
|
(void)data; (void)key; (void)key_len;
|
||||||
|
free(value);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int RouteTable_Init(void) {
|
||||||
|
if (art_tree_init(&route_table_v4) != 0) return -1;
|
||||||
|
if (art_tree_init(&route_table_v6) != 0) {
|
||||||
|
art_tree_destroy(&route_table_v4);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RouteTable_Cleanup(void) {
|
||||||
|
art_iter(&route_table_v4, free_value_cb, NULL);
|
||||||
|
art_tree_destroy(&route_table_v4);
|
||||||
|
art_iter(&route_table_v6, free_value_cb, NULL);
|
||||||
|
art_tree_destroy(&route_table_v6);
|
||||||
|
}
|
||||||
|
|
||||||
|
int RouteTable_AddRoute_v4(uint32_t destination, uint32_t mask, uint32_t nextHop) {
|
||||||
|
if (destination == 0 || nextHop == 0 || mask == 0) return -1;
|
||||||
|
|
||||||
|
if (destination == 0x7F000001) return -1;
|
||||||
|
|
||||||
|
uint8_t prefix_len = IPUtils_MaskToCIDRv4(mask);
|
||||||
|
uint32_t network = destination & mask;
|
||||||
|
|
||||||
|
uint32_t *value = malloc(sizeof(uint32_t));
|
||||||
|
if (!value) return -1;
|
||||||
|
*value = nextHop;
|
||||||
|
|
||||||
|
unsigned char key[5];
|
||||||
|
build_key_v4(key, network, prefix_len);
|
||||||
|
|
||||||
|
void *old = art_insert(&route_table_v4, key, 5, value);
|
||||||
|
if (old) free(old);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int RouteTable_AddRoute_v6(__uint128_t destination, __uint128_t mask, __uint128_t nextHop) {
|
||||||
|
if (destination == 0 || nextHop == 0 || mask == 0) return -1;
|
||||||
|
|
||||||
|
if (destination == (__uint128_t)1) return -1;
|
||||||
|
|
||||||
|
uint8_t prefix_len = IPUtils_MaskToCIDRv6(mask);
|
||||||
|
__uint128_t network = destination & mask;
|
||||||
|
|
||||||
|
__uint128_t *value = malloc(sizeof(__uint128_t));
|
||||||
|
if (!value) return -1;
|
||||||
|
*value = nextHop;
|
||||||
|
|
||||||
|
unsigned char key[17];
|
||||||
|
build_key_v6(key, network, prefix_len);
|
||||||
|
|
||||||
|
void *old = art_insert(&route_table_v6, key, 17, value);
|
||||||
|
if (old) free(old);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int RouteTable_GetNextHop_v4(uint32_t destination, uint32_t *nextHop) {
|
||||||
|
if (!nextHop) return -1;
|
||||||
|
|
||||||
|
unsigned char key[5];
|
||||||
|
for (int plen = 32; plen >= 0; plen--) {
|
||||||
|
uint32_t mask = IPUtils_CIDRToMaskv4((uint8_t)plen);
|
||||||
|
uint32_t network = destination & mask;
|
||||||
|
build_key_v4(key, network, (uint8_t)plen);
|
||||||
|
|
||||||
|
uint32_t *val = art_search(&route_table_v4, key, 5);
|
||||||
|
if (val) {
|
||||||
|
*nextHop = *val;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int RouteTable_GetNextHop_v6(__uint128_t destination, __uint128_t *nextHop) {
|
||||||
|
if (!nextHop) return -1;
|
||||||
|
|
||||||
|
unsigned char key[17];
|
||||||
|
for (int plen = 128; plen >= 0; plen--) {
|
||||||
|
__uint128_t mask = IPUtils_CIDRToMaskv6((uint8_t)plen);
|
||||||
|
__uint128_t network = destination & mask;
|
||||||
|
build_key_v6(key, network, (uint8_t)plen);
|
||||||
|
|
||||||
|
__uint128_t *val = art_search(&route_table_v6, key, 17);
|
||||||
|
if (val) {
|
||||||
|
*nextHop = *val;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
89
src/iputils.c
Normal file
89
src/iputils.c
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
#include <iputils.h>
|
||||||
|
|
||||||
|
uint32_t IPUtils_CIDRToMaskv4(uint8_t cidr) {
|
||||||
|
if (cidr > 32) { return 0x00; }
|
||||||
|
return (0xFFFFFFFFUL << (32 - cidr)) & 0xFFFFFFFFUL;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t IPUtils_MaskToCIDRv4(uint32_t mask) {
|
||||||
|
uint8_t cidr = 0;
|
||||||
|
for (int i = 31; i >= 0; i--) {
|
||||||
|
if ((mask >> i) & 1) {
|
||||||
|
cidr++;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cidr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IPUtils_MatchPrefixv4(uint32_t ip, uint32_t prefix, uint8_t cidr) {
|
||||||
|
uint32_t mask = IPUtils_CIDRToMaskv4(cidr);
|
||||||
|
return (ip & mask) == (prefix & mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
void IPUtils_PrintIPv4(uint32_t ip, char* buf) {
|
||||||
|
if (buf) {
|
||||||
|
snprintf(buf, 16, "%u.%u.%u.%u",
|
||||||
|
(ip >> 24) & 0xFF,
|
||||||
|
(ip >> 16) & 0xFF,
|
||||||
|
(ip >> 8) & 0xFF,
|
||||||
|
ip & 0xFF);
|
||||||
|
} else {
|
||||||
|
printf("%u.%u.%u.%u",
|
||||||
|
(ip >> 24) & 0xFF,
|
||||||
|
(ip >> 16) & 0xFF,
|
||||||
|
(ip >> 8) & 0xFF,
|
||||||
|
ip & 0xFF);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
__uint128_t IPUtils_CIDRToMaskv6(uint8_t cidr) {
|
||||||
|
if (cidr > 128) { return 0x00; }
|
||||||
|
__uint128_t mask = 0;
|
||||||
|
for (int i = 0; i < cidr; i++) {
|
||||||
|
mask |= ((__uint128_t)1 << (127 - i));
|
||||||
|
}
|
||||||
|
return mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t IPUtils_MaskToCIDRv6(__uint128_t mask) {
|
||||||
|
uint8_t cidr = 0;
|
||||||
|
for (int i = 127; i >= 0; i--) {
|
||||||
|
if ((mask >> i) & 1) {
|
||||||
|
cidr++;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cidr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IPUtils_MatchPrefixv6(__uint128_t ip, __uint128_t prefix, uint8_t cidr) {
|
||||||
|
__uint128_t mask = IPUtils_CIDRToMaskv6(cidr);
|
||||||
|
return (ip & mask) == (prefix & mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
void IPUtils_PrintIPv6(__uint128_t ip, char* buf) {
|
||||||
|
if (buf) {
|
||||||
|
snprintf(buf, 40, "%x:%x:%x:%x:%x:%x:%x:%x",
|
||||||
|
(unsigned int)((ip >> 112) & 0xFFFF),
|
||||||
|
(unsigned int)((ip >> 96) & 0xFFFF),
|
||||||
|
(unsigned int)((ip >> 80) & 0xFFFF),
|
||||||
|
(unsigned int)((ip >> 64) & 0xFFFF),
|
||||||
|
(unsigned int)((ip >> 48) & 0xFFFF),
|
||||||
|
(unsigned int)((ip >> 32) & 0xFFFF),
|
||||||
|
(unsigned int)((ip >> 16) & 0xFFFF),
|
||||||
|
(unsigned int)(ip & 0xFFFF));
|
||||||
|
} else {
|
||||||
|
printf("%x:%x:%x:%x:%x:%x:%x:%x",
|
||||||
|
(unsigned int)((ip >> 112) & 0xFFFF),
|
||||||
|
(unsigned int)((ip >> 96) & 0xFFFF),
|
||||||
|
(unsigned int)((ip >> 80) & 0xFFFF),
|
||||||
|
(unsigned int)((ip >> 64) & 0xFFFF),
|
||||||
|
(unsigned int)((ip >> 48) & 0xFFFF),
|
||||||
|
(unsigned int)((ip >> 32) & 0xFFFF),
|
||||||
|
(unsigned int)((ip >> 16) & 0xFFFF),
|
||||||
|
(unsigned int)(ip & 0xFFFF));
|
||||||
|
}
|
||||||
|
}
|
||||||
975
src/libart/art.c
Normal file
975
src/libart/art.c
Normal file
@@ -0,0 +1,975 @@
|
|||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <strings.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <libart/art.h>
|
||||||
|
|
||||||
|
#ifdef __i386__
|
||||||
|
#include <emmintrin.h>
|
||||||
|
#else
|
||||||
|
#ifdef __amd64__
|
||||||
|
#include <emmintrin.h>
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Macros to manipulate pointer tags
|
||||||
|
*/
|
||||||
|
#define IS_LEAF(x) (((uintptr_t)x & 1))
|
||||||
|
#define SET_LEAF(x) ((void*)((uintptr_t)x | 1))
|
||||||
|
#define LEAF_RAW(x) ((art_leaf*)((void*)((uintptr_t)x & ~1)))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allocates a node of the given type,
|
||||||
|
* initializes to zero and sets the type.
|
||||||
|
*/
|
||||||
|
static art_node* alloc_node(uint8_t type) {
|
||||||
|
art_node* n;
|
||||||
|
switch (type) {
|
||||||
|
case NODE4:
|
||||||
|
n = (art_node*)calloc(1, sizeof(art_node4));
|
||||||
|
break;
|
||||||
|
case NODE16:
|
||||||
|
n = (art_node*)calloc(1, sizeof(art_node16));
|
||||||
|
break;
|
||||||
|
case NODE48:
|
||||||
|
n = (art_node*)calloc(1, sizeof(art_node48));
|
||||||
|
break;
|
||||||
|
case NODE256:
|
||||||
|
n = (art_node*)calloc(1, sizeof(art_node256));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
n->type = type;
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes an ART tree
|
||||||
|
* @return 0 on success.
|
||||||
|
*/
|
||||||
|
int art_tree_init(art_tree *t) {
|
||||||
|
t->root = NULL;
|
||||||
|
t->size = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recursively destroys the tree
|
||||||
|
static void destroy_node(art_node *n) {
|
||||||
|
// Break if null
|
||||||
|
if (!n) return;
|
||||||
|
|
||||||
|
// Special case leafs
|
||||||
|
if (IS_LEAF(n)) {
|
||||||
|
free(LEAF_RAW(n));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle each node type
|
||||||
|
int i, idx;
|
||||||
|
union {
|
||||||
|
art_node4 *p1;
|
||||||
|
art_node16 *p2;
|
||||||
|
art_node48 *p3;
|
||||||
|
art_node256 *p4;
|
||||||
|
} p;
|
||||||
|
switch (n->type) {
|
||||||
|
case NODE4:
|
||||||
|
p.p1 = (art_node4*)n;
|
||||||
|
for (i=0;i<n->num_children;i++) {
|
||||||
|
destroy_node(p.p1->children[i]);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NODE16:
|
||||||
|
p.p2 = (art_node16*)n;
|
||||||
|
for (i=0;i<n->num_children;i++) {
|
||||||
|
destroy_node(p.p2->children[i]);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NODE48:
|
||||||
|
p.p3 = (art_node48*)n;
|
||||||
|
for (i=0;i<256;i++) {
|
||||||
|
idx = ((art_node48*)n)->keys[i];
|
||||||
|
if (!idx) continue;
|
||||||
|
destroy_node(p.p3->children[idx-1]);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NODE256:
|
||||||
|
p.p4 = (art_node256*)n;
|
||||||
|
for (i=0;i<256;i++) {
|
||||||
|
if (p.p4->children[i])
|
||||||
|
destroy_node(p.p4->children[i]);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free ourself on the way up
|
||||||
|
free(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destroys an ART tree
|
||||||
|
* @return 0 on success.
|
||||||
|
*/
|
||||||
|
int art_tree_destroy(art_tree *t) {
|
||||||
|
destroy_node(t->root);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the size of the ART tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BROKEN_GCC_C99_INLINE
|
||||||
|
extern inline uint64_t art_size(art_tree *t);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static art_node** find_child(art_node *n, unsigned char c) {
|
||||||
|
int i, mask, bitfield;
|
||||||
|
union {
|
||||||
|
art_node4 *p1;
|
||||||
|
art_node16 *p2;
|
||||||
|
art_node48 *p3;
|
||||||
|
art_node256 *p4;
|
||||||
|
} p;
|
||||||
|
switch (n->type) {
|
||||||
|
case NODE4:
|
||||||
|
p.p1 = (art_node4*)n;
|
||||||
|
for (i=0 ; i < n->num_children; i++) {
|
||||||
|
/* this cast works around a bug in gcc 5.1 when unrolling loops
|
||||||
|
* https://gcc.gnu.org/bugzilla/show_bug.cgi?id=59124
|
||||||
|
*/
|
||||||
|
if (((unsigned char*)p.p1->keys)[i] == c)
|
||||||
|
return &p.p1->children[i];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
{
|
||||||
|
case NODE16:
|
||||||
|
p.p2 = (art_node16*)n;
|
||||||
|
|
||||||
|
// support non-86 architectures
|
||||||
|
#ifdef __i386__
|
||||||
|
// Compare the key to all 16 stored keys
|
||||||
|
__m128i cmp;
|
||||||
|
cmp = _mm_cmpeq_epi8(_mm_set1_epi8(c),
|
||||||
|
_mm_loadu_si128((__m128i*)p.p2->keys));
|
||||||
|
|
||||||
|
// Use a mask to ignore children that don't exist
|
||||||
|
mask = (1 << n->num_children) - 1;
|
||||||
|
bitfield = _mm_movemask_epi8(cmp) & mask;
|
||||||
|
#else
|
||||||
|
#ifdef __amd64__
|
||||||
|
// Compare the key to all 16 stored keys
|
||||||
|
__m128i cmp;
|
||||||
|
cmp = _mm_cmpeq_epi8(_mm_set1_epi8(c),
|
||||||
|
_mm_loadu_si128((__m128i*)p.p2->keys));
|
||||||
|
|
||||||
|
// Use a mask to ignore children that don't exist
|
||||||
|
mask = (1 << n->num_children) - 1;
|
||||||
|
bitfield = _mm_movemask_epi8(cmp) & mask;
|
||||||
|
#else
|
||||||
|
// Compare the key to all 16 stored keys
|
||||||
|
bitfield = 0;
|
||||||
|
for (i = 0; i < 16; ++i) {
|
||||||
|
if (p.p2->keys[i] == c)
|
||||||
|
bitfield |= (1 << i);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use a mask to ignore children that don't exist
|
||||||
|
mask = (1 << n->num_children) - 1;
|
||||||
|
bitfield &= mask;
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If we have a match (any bit set) then we can
|
||||||
|
* return the pointer match using ctz to get
|
||||||
|
* the index.
|
||||||
|
*/
|
||||||
|
if (bitfield)
|
||||||
|
return &p.p2->children[__builtin_ctz(bitfield)];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case NODE48:
|
||||||
|
p.p3 = (art_node48*)n;
|
||||||
|
i = p.p3->keys[c];
|
||||||
|
if (i)
|
||||||
|
return &p.p3->children[i-1];
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NODE256:
|
||||||
|
p.p4 = (art_node256*)n;
|
||||||
|
if (p.p4->children[c])
|
||||||
|
return &p.p4->children[c];
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simple inlined if
|
||||||
|
static inline int min(int a, int b) {
|
||||||
|
return (a < b) ? a : b;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of prefix characters shared between
|
||||||
|
* the key and node.
|
||||||
|
*/
|
||||||
|
static int check_prefix(const art_node *n, const unsigned char *key, int key_len, int depth) {
|
||||||
|
int max_cmp = min(min(n->partial_len, MAX_PREFIX_LEN), key_len - depth);
|
||||||
|
int idx;
|
||||||
|
for (idx=0; idx < max_cmp; idx++) {
|
||||||
|
if (n->partial[idx] != key[depth+idx])
|
||||||
|
return idx;
|
||||||
|
}
|
||||||
|
return idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a leaf matches
|
||||||
|
* @return 0 on success.
|
||||||
|
*/
|
||||||
|
static int leaf_matches(const art_leaf *n, const unsigned char *key, int key_len, int depth) {
|
||||||
|
(void)depth;
|
||||||
|
// Fail if the key lengths are different
|
||||||
|
if (n->key_len != (uint32_t)key_len) return 1;
|
||||||
|
|
||||||
|
// Compare the keys starting at the depth
|
||||||
|
return memcmp(n->key, key, key_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Searches for a value in the ART tree
|
||||||
|
* @arg t The tree
|
||||||
|
* @arg key The key
|
||||||
|
* @arg key_len The length of the key
|
||||||
|
* @return NULL if the item was not found, otherwise
|
||||||
|
* the value pointer is returned.
|
||||||
|
*/
|
||||||
|
void* art_search(const art_tree *t, const unsigned char *key, int key_len) {
|
||||||
|
art_node **child;
|
||||||
|
art_node *n = t->root;
|
||||||
|
int prefix_len, depth = 0;
|
||||||
|
while (n) {
|
||||||
|
// Might be a leaf
|
||||||
|
if (IS_LEAF(n)) {
|
||||||
|
n = (art_node*)LEAF_RAW(n);
|
||||||
|
// Check if the expanded path matches
|
||||||
|
if (!leaf_matches((art_leaf*)n, key, key_len, depth)) {
|
||||||
|
return ((art_leaf*)n)->value;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bail if the prefix does not match
|
||||||
|
if (n->partial_len) {
|
||||||
|
prefix_len = check_prefix(n, key, key_len, depth);
|
||||||
|
if (prefix_len != min(MAX_PREFIX_LEN, n->partial_len))
|
||||||
|
return NULL;
|
||||||
|
depth = depth + n->partial_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recursively search
|
||||||
|
child = find_child(n, key[depth]);
|
||||||
|
n = (child) ? *child : NULL;
|
||||||
|
depth++;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the minimum leaf under a node
|
||||||
|
static art_leaf* minimum(const art_node *n) {
|
||||||
|
// Handle base cases
|
||||||
|
if (!n) return NULL;
|
||||||
|
if (IS_LEAF(n)) return LEAF_RAW(n);
|
||||||
|
|
||||||
|
int idx;
|
||||||
|
switch (n->type) {
|
||||||
|
case NODE4:
|
||||||
|
return minimum(((const art_node4*)n)->children[0]);
|
||||||
|
case NODE16:
|
||||||
|
return minimum(((const art_node16*)n)->children[0]);
|
||||||
|
case NODE48:
|
||||||
|
idx=0;
|
||||||
|
while (!((const art_node48*)n)->keys[idx]) idx++;
|
||||||
|
idx = ((const art_node48*)n)->keys[idx] - 1;
|
||||||
|
return minimum(((const art_node48*)n)->children[idx]);
|
||||||
|
case NODE256:
|
||||||
|
idx=0;
|
||||||
|
while (!((const art_node256*)n)->children[idx]) idx++;
|
||||||
|
return minimum(((const art_node256*)n)->children[idx]);
|
||||||
|
default:
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the maximum leaf under a node
|
||||||
|
static art_leaf* maximum(const art_node *n) {
|
||||||
|
// Handle base cases
|
||||||
|
if (!n) return NULL;
|
||||||
|
if (IS_LEAF(n)) return LEAF_RAW(n);
|
||||||
|
|
||||||
|
int idx;
|
||||||
|
switch (n->type) {
|
||||||
|
case NODE4:
|
||||||
|
return maximum(((const art_node4*)n)->children[n->num_children-1]);
|
||||||
|
case NODE16:
|
||||||
|
return maximum(((const art_node16*)n)->children[n->num_children-1]);
|
||||||
|
case NODE48:
|
||||||
|
idx=255;
|
||||||
|
while (!((const art_node48*)n)->keys[idx]) idx--;
|
||||||
|
idx = ((const art_node48*)n)->keys[idx] - 1;
|
||||||
|
return maximum(((const art_node48*)n)->children[idx]);
|
||||||
|
case NODE256:
|
||||||
|
idx=255;
|
||||||
|
while (!((const art_node256*)n)->children[idx]) idx--;
|
||||||
|
return maximum(((const art_node256*)n)->children[idx]);
|
||||||
|
default:
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the minimum valued leaf
|
||||||
|
*/
|
||||||
|
art_leaf* art_minimum(art_tree *t) {
|
||||||
|
return minimum((art_node*)t->root);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the maximum valued leaf
|
||||||
|
*/
|
||||||
|
art_leaf* art_maximum(art_tree *t) {
|
||||||
|
return maximum((art_node*)t->root);
|
||||||
|
}
|
||||||
|
|
||||||
|
static art_leaf* make_leaf(const unsigned char *key, int key_len, void *value) {
|
||||||
|
art_leaf *l = (art_leaf*)calloc(1, sizeof(art_leaf)+key_len);
|
||||||
|
l->value = value;
|
||||||
|
l->key_len = key_len;
|
||||||
|
memcpy(l->key, key, key_len);
|
||||||
|
return l;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int longest_common_prefix(art_leaf *l1, art_leaf *l2, int depth) {
|
||||||
|
int max_cmp = min(l1->key_len, l2->key_len) - depth;
|
||||||
|
int idx;
|
||||||
|
for (idx=0; idx < max_cmp; idx++) {
|
||||||
|
if (l1->key[depth+idx] != l2->key[depth+idx])
|
||||||
|
return idx;
|
||||||
|
}
|
||||||
|
return idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void copy_header(art_node *dest, art_node *src) {
|
||||||
|
dest->num_children = src->num_children;
|
||||||
|
dest->partial_len = src->partial_len;
|
||||||
|
memcpy(dest->partial, src->partial, min(MAX_PREFIX_LEN, src->partial_len));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void add_child256(art_node256 *n, art_node **ref, unsigned char c, void *child) {
|
||||||
|
(void)ref;
|
||||||
|
n->n.num_children++;
|
||||||
|
n->children[c] = (art_node*)child;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void add_child48(art_node48 *n, art_node **ref, unsigned char c, void *child) {
|
||||||
|
if (n->n.num_children < 48) {
|
||||||
|
int pos = 0;
|
||||||
|
while (n->children[pos]) pos++;
|
||||||
|
n->children[pos] = (art_node*)child;
|
||||||
|
n->keys[c] = pos + 1;
|
||||||
|
n->n.num_children++;
|
||||||
|
} else {
|
||||||
|
art_node256 *new_node = (art_node256*)alloc_node(NODE256);
|
||||||
|
for (int i=0;i<256;i++) {
|
||||||
|
if (n->keys[i]) {
|
||||||
|
new_node->children[i] = n->children[n->keys[i] - 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
copy_header((art_node*)new_node, (art_node*)n);
|
||||||
|
*ref = (art_node*)new_node;
|
||||||
|
free(n);
|
||||||
|
add_child256(new_node, ref, c, child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void add_child16(art_node16 *n, art_node **ref, unsigned char c, void *child) {
|
||||||
|
if (n->n.num_children < 16) {
|
||||||
|
unsigned mask = (1 << n->n.num_children) - 1;
|
||||||
|
|
||||||
|
// support non-x86 architectures
|
||||||
|
#ifdef __i386__
|
||||||
|
__m128i cmp;
|
||||||
|
|
||||||
|
// Compare the key to all 16 stored keys
|
||||||
|
cmp = _mm_cmplt_epi8(_mm_set1_epi8(c),
|
||||||
|
_mm_loadu_si128((__m128i*)n->keys));
|
||||||
|
|
||||||
|
// Use a mask to ignore children that don't exist
|
||||||
|
unsigned bitfield = _mm_movemask_epi8(cmp) & mask;
|
||||||
|
#else
|
||||||
|
#ifdef __amd64__
|
||||||
|
__m128i cmp;
|
||||||
|
|
||||||
|
// Compare the key to all 16 stored keys
|
||||||
|
cmp = _mm_cmplt_epi8(_mm_set1_epi8(c),
|
||||||
|
_mm_loadu_si128((__m128i*)n->keys));
|
||||||
|
|
||||||
|
// Use a mask to ignore children that don't exist
|
||||||
|
unsigned bitfield = _mm_movemask_epi8(cmp) & mask;
|
||||||
|
#else
|
||||||
|
// Compare the key to all 16 stored keys
|
||||||
|
unsigned bitfield = 0;
|
||||||
|
for (short i = 0; i < 16; ++i) {
|
||||||
|
if (c < n->keys[i])
|
||||||
|
bitfield |= (1 << i);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use a mask to ignore children that don't exist
|
||||||
|
bitfield &= mask;
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Check if less than any
|
||||||
|
unsigned idx;
|
||||||
|
if (bitfield) {
|
||||||
|
idx = __builtin_ctz(bitfield);
|
||||||
|
memmove(n->keys+idx+1,n->keys+idx,n->n.num_children-idx);
|
||||||
|
memmove(n->children+idx+1,n->children+idx,
|
||||||
|
(n->n.num_children-idx)*sizeof(void*));
|
||||||
|
} else
|
||||||
|
idx = n->n.num_children;
|
||||||
|
|
||||||
|
// Set the child
|
||||||
|
n->keys[idx] = c;
|
||||||
|
n->children[idx] = (art_node*)child;
|
||||||
|
n->n.num_children++;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
art_node48 *new_node = (art_node48*)alloc_node(NODE48);
|
||||||
|
|
||||||
|
// Copy the child pointers and populate the key map
|
||||||
|
memcpy(new_node->children, n->children,
|
||||||
|
sizeof(void*)*n->n.num_children);
|
||||||
|
for (int i=0;i<n->n.num_children;i++) {
|
||||||
|
new_node->keys[n->keys[i]] = i + 1;
|
||||||
|
}
|
||||||
|
copy_header((art_node*)new_node, (art_node*)n);
|
||||||
|
*ref = (art_node*)new_node;
|
||||||
|
free(n);
|
||||||
|
add_child48(new_node, ref, c, child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void add_child4(art_node4 *n, art_node **ref, unsigned char c, void *child) {
|
||||||
|
if (n->n.num_children < 4) {
|
||||||
|
int idx;
|
||||||
|
for (idx=0; idx < n->n.num_children; idx++) {
|
||||||
|
if (c < n->keys[idx]) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shift to make room
|
||||||
|
memmove(n->keys+idx+1, n->keys+idx, n->n.num_children - idx);
|
||||||
|
memmove(n->children+idx+1, n->children+idx,
|
||||||
|
(n->n.num_children - idx)*sizeof(void*));
|
||||||
|
|
||||||
|
// Insert element
|
||||||
|
n->keys[idx] = c;
|
||||||
|
n->children[idx] = (art_node*)child;
|
||||||
|
n->n.num_children++;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
art_node16 *new_node = (art_node16*)alloc_node(NODE16);
|
||||||
|
|
||||||
|
// Copy the child pointers and the key map
|
||||||
|
memcpy(new_node->children, n->children,
|
||||||
|
sizeof(void*)*n->n.num_children);
|
||||||
|
memcpy(new_node->keys, n->keys,
|
||||||
|
sizeof(unsigned char)*n->n.num_children);
|
||||||
|
copy_header((art_node*)new_node, (art_node*)n);
|
||||||
|
*ref = (art_node*)new_node;
|
||||||
|
free(n);
|
||||||
|
add_child16(new_node, ref, c, child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void add_child(art_node *n, art_node **ref, unsigned char c, void *child) {
|
||||||
|
switch (n->type) {
|
||||||
|
case NODE4:
|
||||||
|
return add_child4((art_node4*)n, ref, c, child);
|
||||||
|
case NODE16:
|
||||||
|
return add_child16((art_node16*)n, ref, c, child);
|
||||||
|
case NODE48:
|
||||||
|
return add_child48((art_node48*)n, ref, c, child);
|
||||||
|
case NODE256:
|
||||||
|
return add_child256((art_node256*)n, ref, c, child);
|
||||||
|
default:
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the index at which the prefixes mismatch
|
||||||
|
*/
|
||||||
|
static int prefix_mismatch(const art_node *n, const unsigned char *key, int key_len, int depth) {
|
||||||
|
int max_cmp = min(min(MAX_PREFIX_LEN, n->partial_len), key_len - depth);
|
||||||
|
int idx;
|
||||||
|
for (idx=0; idx < max_cmp; idx++) {
|
||||||
|
if (n->partial[idx] != key[depth+idx])
|
||||||
|
return idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the prefix is short we can avoid finding a leaf
|
||||||
|
if (n->partial_len > MAX_PREFIX_LEN) {
|
||||||
|
// Prefix is longer than what we've checked, find a leaf
|
||||||
|
art_leaf *l = minimum(n);
|
||||||
|
max_cmp = min(l->key_len, key_len)- depth;
|
||||||
|
for (; idx < max_cmp; idx++) {
|
||||||
|
if (l->key[idx+depth] != key[depth+idx])
|
||||||
|
return idx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void* recursive_insert(art_node *n, art_node **ref, const unsigned char *key, int key_len, void *value, int depth, int *old, int replace) {
|
||||||
|
// If we are at a NULL node, inject a leaf
|
||||||
|
if (!n) {
|
||||||
|
*ref = (art_node*)SET_LEAF(make_leaf(key, key_len, value));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we are at a leaf, we need to replace it with a node
|
||||||
|
if (IS_LEAF(n)) {
|
||||||
|
art_leaf *l = LEAF_RAW(n);
|
||||||
|
|
||||||
|
// Check if we are updating an existing value
|
||||||
|
if (!leaf_matches(l, key, key_len, depth)) {
|
||||||
|
*old = 1;
|
||||||
|
void *old_val = l->value;
|
||||||
|
if(replace) l->value = value;
|
||||||
|
return old_val;
|
||||||
|
}
|
||||||
|
|
||||||
|
// New value, we must split the leaf into a node4
|
||||||
|
art_node4 *new_node = (art_node4*)alloc_node(NODE4);
|
||||||
|
|
||||||
|
// Create a new leaf
|
||||||
|
art_leaf *l2 = make_leaf(key, key_len, value);
|
||||||
|
|
||||||
|
// Determine longest prefix
|
||||||
|
int longest_prefix = longest_common_prefix(l, l2, depth);
|
||||||
|
new_node->n.partial_len = longest_prefix;
|
||||||
|
memcpy(new_node->n.partial, key+depth, min(MAX_PREFIX_LEN, longest_prefix));
|
||||||
|
// Add the leafs to the new node4
|
||||||
|
*ref = (art_node*)new_node;
|
||||||
|
add_child4(new_node, ref, l->key[depth+longest_prefix], SET_LEAF(l));
|
||||||
|
add_child4(new_node, ref, l2->key[depth+longest_prefix], SET_LEAF(l2));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if given node has a prefix
|
||||||
|
if (n->partial_len) {
|
||||||
|
// Determine if the prefixes differ, since we need to split
|
||||||
|
int prefix_diff = prefix_mismatch(n, key, key_len, depth);
|
||||||
|
if ((uint32_t)prefix_diff >= n->partial_len) {
|
||||||
|
depth += n->partial_len;
|
||||||
|
goto RECURSE_SEARCH;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new node
|
||||||
|
art_node4 *new_node = (art_node4*)alloc_node(NODE4);
|
||||||
|
*ref = (art_node*)new_node;
|
||||||
|
new_node->n.partial_len = prefix_diff;
|
||||||
|
memcpy(new_node->n.partial, n->partial, min(MAX_PREFIX_LEN, prefix_diff));
|
||||||
|
|
||||||
|
// Adjust the prefix of the old node
|
||||||
|
if (n->partial_len <= MAX_PREFIX_LEN) {
|
||||||
|
add_child4(new_node, ref, n->partial[prefix_diff], n);
|
||||||
|
n->partial_len -= (prefix_diff+1);
|
||||||
|
memmove(n->partial, n->partial+prefix_diff+1,
|
||||||
|
min(MAX_PREFIX_LEN, n->partial_len));
|
||||||
|
} else {
|
||||||
|
n->partial_len -= (prefix_diff+1);
|
||||||
|
art_leaf *l = minimum(n);
|
||||||
|
add_child4(new_node, ref, l->key[depth+prefix_diff], n);
|
||||||
|
memcpy(n->partial, l->key+depth+prefix_diff+1,
|
||||||
|
min(MAX_PREFIX_LEN, n->partial_len));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert the new leaf
|
||||||
|
art_leaf *l = make_leaf(key, key_len, value);
|
||||||
|
add_child4(new_node, ref, key[depth+prefix_diff], SET_LEAF(l));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
RECURSE_SEARCH:;
|
||||||
|
|
||||||
|
// Find a child to recurse to
|
||||||
|
art_node **child = find_child(n, key[depth]);
|
||||||
|
if (child) {
|
||||||
|
return recursive_insert(*child, child, key, key_len, value, depth+1, old, replace);
|
||||||
|
}
|
||||||
|
|
||||||
|
// No child, node goes within us
|
||||||
|
art_leaf *l = make_leaf(key, key_len, value);
|
||||||
|
add_child(n, ref, key[depth], SET_LEAF(l));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* inserts a new value into the art tree
|
||||||
|
* @arg t the tree
|
||||||
|
* @arg key the key
|
||||||
|
* @arg key_len the length of the key
|
||||||
|
* @arg value opaque value.
|
||||||
|
* @return null if the item was newly inserted, otherwise
|
||||||
|
* the old value pointer is returned.
|
||||||
|
*/
|
||||||
|
void* art_insert(art_tree *t, const unsigned char *key, int key_len, void *value) {
|
||||||
|
int old_val = 0;
|
||||||
|
void *old = recursive_insert(t->root, &t->root, key, key_len, value, 0, &old_val, 1);
|
||||||
|
if (!old_val) t->size++;
|
||||||
|
return old;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* inserts a new value into the art tree (no replace)
|
||||||
|
* @arg t the tree
|
||||||
|
* @arg key the key
|
||||||
|
* @arg key_len the length of the key
|
||||||
|
* @arg value opaque value.
|
||||||
|
* @return null if the item was newly inserted, otherwise
|
||||||
|
* the old value pointer is returned.
|
||||||
|
*/
|
||||||
|
void* art_insert_no_replace(art_tree *t, const unsigned char *key, int key_len, void *value) {
|
||||||
|
int old_val = 0;
|
||||||
|
void *old = recursive_insert(t->root, &t->root, key, key_len, value, 0, &old_val, 0);
|
||||||
|
if (!old_val) t->size++;
|
||||||
|
return old;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void remove_child256(art_node256 *n, art_node **ref, unsigned char c) {
|
||||||
|
n->children[c] = NULL;
|
||||||
|
n->n.num_children--;
|
||||||
|
|
||||||
|
// Resize to a node48 on underflow, not immediately to prevent
|
||||||
|
// trashing if we sit on the 48/49 boundary
|
||||||
|
if (n->n.num_children == 37) {
|
||||||
|
art_node48 *new_node = (art_node48*)alloc_node(NODE48);
|
||||||
|
*ref = (art_node*)new_node;
|
||||||
|
copy_header((art_node*)new_node, (art_node*)n);
|
||||||
|
|
||||||
|
int pos = 0;
|
||||||
|
for (int i=0;i<256;i++) {
|
||||||
|
if (n->children[i]) {
|
||||||
|
new_node->children[pos] = n->children[i];
|
||||||
|
new_node->keys[i] = pos + 1;
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void remove_child48(art_node48 *n, art_node **ref, unsigned char c) {
|
||||||
|
int pos = n->keys[c];
|
||||||
|
n->keys[c] = 0;
|
||||||
|
n->children[pos-1] = NULL;
|
||||||
|
n->n.num_children--;
|
||||||
|
|
||||||
|
if (n->n.num_children == 12) {
|
||||||
|
art_node16 *new_node = (art_node16*)alloc_node(NODE16);
|
||||||
|
*ref = (art_node*)new_node;
|
||||||
|
copy_header((art_node*)new_node, (art_node*)n);
|
||||||
|
|
||||||
|
int child = 0;
|
||||||
|
for (int i=0;i<256;i++) {
|
||||||
|
pos = n->keys[i];
|
||||||
|
if (pos) {
|
||||||
|
new_node->keys[child] = i;
|
||||||
|
new_node->children[child] = n->children[pos - 1];
|
||||||
|
child++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void remove_child16(art_node16 *n, art_node **ref, art_node **l) {
|
||||||
|
int pos = l - n->children;
|
||||||
|
memmove(n->keys+pos, n->keys+pos+1, n->n.num_children - 1 - pos);
|
||||||
|
memmove(n->children+pos, n->children+pos+1, (n->n.num_children - 1 - pos)*sizeof(void*));
|
||||||
|
n->n.num_children--;
|
||||||
|
|
||||||
|
if (n->n.num_children == 3) {
|
||||||
|
art_node4 *new_node = (art_node4*)alloc_node(NODE4);
|
||||||
|
*ref = (art_node*)new_node;
|
||||||
|
copy_header((art_node*)new_node, (art_node*)n);
|
||||||
|
memcpy(new_node->keys, n->keys, 4);
|
||||||
|
memcpy(new_node->children, n->children, 4*sizeof(void*));
|
||||||
|
free(n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void remove_child4(art_node4 *n, art_node **ref, art_node **l) {
|
||||||
|
int pos = l - n->children;
|
||||||
|
memmove(n->keys+pos, n->keys+pos+1, n->n.num_children - 1 - pos);
|
||||||
|
memmove(n->children+pos, n->children+pos+1, (n->n.num_children - 1 - pos)*sizeof(void*));
|
||||||
|
n->n.num_children--;
|
||||||
|
|
||||||
|
// Remove nodes with only a single child
|
||||||
|
if (n->n.num_children == 1) {
|
||||||
|
art_node *child = n->children[0];
|
||||||
|
if (!IS_LEAF(child)) {
|
||||||
|
// Concatenate the prefixes
|
||||||
|
int prefix = n->n.partial_len;
|
||||||
|
if (prefix < MAX_PREFIX_LEN) {
|
||||||
|
n->n.partial[prefix] = n->keys[0];
|
||||||
|
prefix++;
|
||||||
|
}
|
||||||
|
if (prefix < MAX_PREFIX_LEN) {
|
||||||
|
int sub_prefix = min(child->partial_len, MAX_PREFIX_LEN - prefix);
|
||||||
|
memcpy(n->n.partial+prefix, child->partial, sub_prefix);
|
||||||
|
prefix += sub_prefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store the prefix in the child
|
||||||
|
memcpy(child->partial, n->n.partial, min(prefix, MAX_PREFIX_LEN));
|
||||||
|
child->partial_len += n->n.partial_len + 1;
|
||||||
|
}
|
||||||
|
*ref = child;
|
||||||
|
free(n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void remove_child(art_node *n, art_node **ref, unsigned char c, art_node **l) {
|
||||||
|
switch (n->type) {
|
||||||
|
case NODE4:
|
||||||
|
return remove_child4((art_node4*)n, ref, l);
|
||||||
|
case NODE16:
|
||||||
|
return remove_child16((art_node16*)n, ref, l);
|
||||||
|
case NODE48:
|
||||||
|
return remove_child48((art_node48*)n, ref, c);
|
||||||
|
case NODE256:
|
||||||
|
return remove_child256((art_node256*)n, ref, c);
|
||||||
|
default:
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static art_leaf* recursive_delete(art_node *n, art_node **ref, const unsigned char *key, int key_len, int depth) {
|
||||||
|
// Search terminated
|
||||||
|
if (!n) return NULL;
|
||||||
|
|
||||||
|
// Handle hitting a leaf node
|
||||||
|
if (IS_LEAF(n)) {
|
||||||
|
art_leaf *l = LEAF_RAW(n);
|
||||||
|
if (!leaf_matches(l, key, key_len, depth)) {
|
||||||
|
*ref = NULL;
|
||||||
|
return l;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bail if the prefix does not match
|
||||||
|
if (n->partial_len) {
|
||||||
|
int prefix_len = check_prefix(n, key, key_len, depth);
|
||||||
|
if (prefix_len != min(MAX_PREFIX_LEN, n->partial_len)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
depth = depth + n->partial_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find child node
|
||||||
|
art_node **child = find_child(n, key[depth]);
|
||||||
|
if (!child) return NULL;
|
||||||
|
|
||||||
|
// If the child is leaf, delete from this node
|
||||||
|
if (IS_LEAF(*child)) {
|
||||||
|
art_leaf *l = LEAF_RAW(*child);
|
||||||
|
if (!leaf_matches(l, key, key_len, depth)) {
|
||||||
|
remove_child(n, ref, key[depth], child);
|
||||||
|
return l;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
// Recurse
|
||||||
|
} else {
|
||||||
|
return recursive_delete(*child, child, key, key_len, depth+1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes a value from the ART tree
|
||||||
|
* @arg t The tree
|
||||||
|
* @arg key The key
|
||||||
|
* @arg key_len The length of the key
|
||||||
|
* @return NULL if the item was not found, otherwise
|
||||||
|
* the value pointer is returned.
|
||||||
|
*/
|
||||||
|
void* art_delete(art_tree *t, const unsigned char *key, int key_len) {
|
||||||
|
art_leaf *l = recursive_delete(t->root, &t->root, key, key_len, 0);
|
||||||
|
if (l) {
|
||||||
|
t->size--;
|
||||||
|
void *old = l->value;
|
||||||
|
free(l);
|
||||||
|
return old;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recursively iterates over the tree
|
||||||
|
static int recursive_iter(art_node *n, art_callback cb, void *data) {
|
||||||
|
// Handle base cases
|
||||||
|
if (!n) return 0;
|
||||||
|
if (IS_LEAF(n)) {
|
||||||
|
art_leaf *l = LEAF_RAW(n);
|
||||||
|
return cb(data, (const unsigned char*)l->key, l->key_len, l->value);
|
||||||
|
}
|
||||||
|
|
||||||
|
int idx, res;
|
||||||
|
switch (n->type) {
|
||||||
|
case NODE4:
|
||||||
|
for (int i=0; i < n->num_children; i++) {
|
||||||
|
res = recursive_iter(((art_node4*)n)->children[i], cb, data);
|
||||||
|
if (res) return res;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NODE16:
|
||||||
|
for (int i=0; i < n->num_children; i++) {
|
||||||
|
res = recursive_iter(((art_node16*)n)->children[i], cb, data);
|
||||||
|
if (res) return res;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NODE48:
|
||||||
|
for (int i=0; i < 256; i++) {
|
||||||
|
idx = ((art_node48*)n)->keys[i];
|
||||||
|
if (!idx) continue;
|
||||||
|
|
||||||
|
res = recursive_iter(((art_node48*)n)->children[idx-1], cb, data);
|
||||||
|
if (res) return res;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NODE256:
|
||||||
|
for (int i=0; i < 256; i++) {
|
||||||
|
if (!((art_node256*)n)->children[i]) continue;
|
||||||
|
res = recursive_iter(((art_node256*)n)->children[i], cb, data);
|
||||||
|
if (res) return res;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Iterates through the entries pairs in the map,
|
||||||
|
* invoking a callback for each. The call back gets a
|
||||||
|
* key, value for each and returns an integer stop value.
|
||||||
|
* If the callback returns non-zero, then the iteration stops.
|
||||||
|
* @arg t The tree to iterate over
|
||||||
|
* @arg cb The callback function to invoke
|
||||||
|
* @arg data Opaque handle passed to the callback
|
||||||
|
* @return 0 on success, or the return of the callback.
|
||||||
|
*/
|
||||||
|
int art_iter(art_tree *t, art_callback cb, void *data) {
|
||||||
|
return recursive_iter(t->root, cb, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a leaf prefix matches
|
||||||
|
* @return 0 on success.
|
||||||
|
*/
|
||||||
|
static int leaf_prefix_matches(const art_leaf *n, const unsigned char *prefix, int prefix_len) {
|
||||||
|
// Fail if the key length is too short
|
||||||
|
if (n->key_len < (uint32_t)prefix_len) return 1;
|
||||||
|
|
||||||
|
// Compare the keys
|
||||||
|
return memcmp(n->key, prefix, prefix_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Iterates through the entries pairs in the map,
|
||||||
|
* invoking a callback for each that matches a given prefix.
|
||||||
|
* The call back gets a key, value for each and returns an integer stop value.
|
||||||
|
* If the callback returns non-zero, then the iteration stops.
|
||||||
|
* @arg t The tree to iterate over
|
||||||
|
* @arg prefix The prefix of keys to read
|
||||||
|
* @arg prefix_len The length of the prefix
|
||||||
|
* @arg cb The callback function to invoke
|
||||||
|
* @arg data Opaque handle passed to the callback
|
||||||
|
* @return 0 on success, or the return of the callback.
|
||||||
|
*/
|
||||||
|
int art_iter_prefix(art_tree *t, const unsigned char *key, int key_len, art_callback cb, void *data) {
|
||||||
|
art_node **child;
|
||||||
|
art_node *n = t->root;
|
||||||
|
int prefix_len, depth = 0;
|
||||||
|
while (n) {
|
||||||
|
// Might be a leaf
|
||||||
|
if (IS_LEAF(n)) {
|
||||||
|
n = (art_node*)LEAF_RAW(n);
|
||||||
|
// Check if the expanded path matches
|
||||||
|
if (!leaf_prefix_matches((art_leaf*)n, key, key_len)) {
|
||||||
|
art_leaf *l = (art_leaf*)n;
|
||||||
|
return cb(data, (const unsigned char*)l->key, l->key_len, l->value);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the depth matches the prefix, we need to handle this node
|
||||||
|
if (depth == key_len) {
|
||||||
|
art_leaf *l = minimum(n);
|
||||||
|
if (!leaf_prefix_matches(l, key, key_len))
|
||||||
|
return recursive_iter(n, cb, data);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bail if the prefix does not match
|
||||||
|
if (n->partial_len) {
|
||||||
|
prefix_len = prefix_mismatch(n, key, key_len, depth);
|
||||||
|
|
||||||
|
// Guard if the mis-match is longer than the MAX_PREFIX_LEN
|
||||||
|
if ((uint32_t)prefix_len > n->partial_len) {
|
||||||
|
prefix_len = n->partial_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there is no match, search is terminated
|
||||||
|
if (!prefix_len) {
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
// If we've matched the prefix, iterate on this node
|
||||||
|
} else if (depth + prefix_len == key_len) {
|
||||||
|
return recursive_iter(n, cb, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if there is a full match, go deeper
|
||||||
|
depth = depth + n->partial_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recursively search
|
||||||
|
child = find_child(n, key[depth]);
|
||||||
|
n = (child) ? *child : NULL;
|
||||||
|
depth++;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
89
src/main.c
Normal file
89
src/main.c
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include <forward/routetable.h>
|
||||||
|
#include <forward/if/interface.h>
|
||||||
|
#include <iputils.h>
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
if (RouteTable_Init() < 0) {
|
||||||
|
fprintf(stderr, "Failed to initialize route tables\n");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface_v4_t* iface = Interface_Create_v4("0.0.0.0", NULL, NULL);
|
||||||
|
interface_v6_t* iface6 = Interface_Create_v6("::", NULL, NULL);
|
||||||
|
if (!iface || !iface6) {
|
||||||
|
fprintf(stderr, "Failed to create interfaces\n");
|
||||||
|
RouteTable_Cleanup();
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
route_table_v4_entry_t entry = {.destination = 0x01000000, .mask = 0xFF000000, .nextHop = 0xC0A80101}; // Example route: 1.0.0.0/8 via 192.168.1.1
|
||||||
|
if (RouteTable_AddRoute_v4(entry.destination, entry.mask, entry.nextHop) < 0) {
|
||||||
|
fprintf(stderr, "Failed to add route to IPv4 route table\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
route_table_v4_entry_t entry2 = {.destination = 0x01020000, .mask = 0xFFFF0000, .nextHop = 0xC0A80102}; // Example route: 1.2.0.0/16 via 192.168.1.1 - more specific than the previous one
|
||||||
|
if (RouteTable_AddRoute_v4(entry2.destination, entry2.mask, entry2.nextHop) < 0) {
|
||||||
|
fprintf(stderr, "Failed to add route to IPv4 route table\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get said route
|
||||||
|
uint32_t nextHop;
|
||||||
|
if (RouteTable_GetNextHop_v4(0x01020005, &nextHop) == 0) { // 1.2.0.5 should match the /16 route, not the /8 route
|
||||||
|
char destStr[16], nextHopStr[16];
|
||||||
|
IPUtils_PrintIPv4(0x01020005, destStr);
|
||||||
|
IPUtils_PrintIPv4(nextHop, nextHopStr);
|
||||||
|
printf("Next hop for destination %s is %s\n", destStr, nextHopStr);
|
||||||
|
} else {
|
||||||
|
char destStr[16];
|
||||||
|
IPUtils_PrintIPv4(0x01020005, destStr);
|
||||||
|
fprintf(stderr, "Failed to get next hop for destination %s\n", destStr);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (RouteTable_GetNextHop_v4(0x01010001, &nextHop) == 0) { // 1.1.0.1 should match the /8 route, not the /16 route
|
||||||
|
char destStr[16], nextHopStr[16];
|
||||||
|
IPUtils_PrintIPv4(0x01010001, destStr);
|
||||||
|
IPUtils_PrintIPv4(nextHop, nextHopStr);
|
||||||
|
printf("Next hop for destination %s is %s\n", destStr, nextHopStr);
|
||||||
|
} else {
|
||||||
|
char destStr[16];
|
||||||
|
IPUtils_PrintIPv4(0x01010001, destStr);
|
||||||
|
fprintf(stderr, "Failed to get next hop for destination %s\n", destStr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Some IPv6 routes
|
||||||
|
__uint128_t dest6 = (((__uint128_t)0x20010DB8 << 96) | ((__uint128_t)0x00000000 << 64) | ((__uint128_t)0x00000000 << 32) | (__uint128_t)0x00000001); // 2001:0db8::1
|
||||||
|
__uint128_t mask6 = IPUtils_CIDRToMaskv6(64);
|
||||||
|
__uint128_t nextHop6 = (((__uint128_t)0x20010DB8 << 96) | ((__uint128_t)0x00000000 << 64) | ((__uint128_t)0x00000000 << 32) | (__uint128_t)0x00000002); // 2001:0db8::2
|
||||||
|
if (RouteTable_AddRoute_v6(dest6, mask6, nextHop6) < 0) {
|
||||||
|
fprintf(stderr, "Failed to add route to IPv6 route table\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get said IPv6 route
|
||||||
|
__uint128_t nextHop6Out;
|
||||||
|
if (RouteTable_GetNextHop_v6(dest6, &nextHop6Out) == 0) {
|
||||||
|
char destStr[40], nextHopStr[40];
|
||||||
|
IPUtils_PrintIPv6(dest6, destStr);
|
||||||
|
IPUtils_PrintIPv6(nextHop6Out, nextHopStr);
|
||||||
|
printf("Next hop for destination %s is %s\n", destStr, nextHopStr);
|
||||||
|
} else {
|
||||||
|
char destStr[40];
|
||||||
|
IPUtils_PrintIPv6(dest6, destStr);
|
||||||
|
fprintf(stderr, "Failed to get next hop for destination %s\n", destStr);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
// Main loop can be used for future tasks such as monitoring, CLI, etc.
|
||||||
|
// For now, it just keeps the program running until interrupted.
|
||||||
|
pause();
|
||||||
|
}
|
||||||
|
|
||||||
|
RouteTable_Cleanup();
|
||||||
|
Interface_Cleanup();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user