├── .gitignore ├── go.mod ├── .github └── workflows │ └── go.yml ├── LICENSE ├── README.md ├── netradix.go ├── netradix_test.go ├── radix.h └── radix.c /.gitignore: -------------------------------------------------------------------------------- 1 | _obj/* 2 | *.swp 3 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/thekvs/go-net-radix 2 | 3 | go 1.17 4 | -------------------------------------------------------------------------------- /.github/workflows/go.yml: -------------------------------------------------------------------------------- 1 | name: Go 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | 11 | build: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v2 15 | 16 | - name: Set up Go 17 | uses: actions/setup-go@v2 18 | with: 19 | go-version: 1.17 20 | 21 | - name: Build 22 | run: go build -v ./... 23 | 24 | - name: Test 25 | run: go test -v ./... 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Konstantin Sorokin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## About 2 | 3 | Go programming language (http://golang.org/) bindings for radix tree library for fast subnet (both IPv4 and IPv6) lookups. 4 | 5 | ## Installing 6 | ``` 7 | $ go get github.com/thekvs/go-net-radix 8 | ``` 9 | 10 | After this command *go-net-radix* library is ready to use and will be located in: 11 | ``` 12 | $GOROOT/src/pkg/github.com/thekvs/go-net-radix 13 | ``` 14 | 15 | ## Basic usage example 16 | ```go 17 | 18 | package main 19 | 20 | import ( 21 | "github.com/thekvs/go-net-radix" 22 | 23 | "fmt" 24 | "log" 25 | ) 26 | 27 | func main() { 28 | rtree, err := netradix.NewNetRadixTree() 29 | if err != nil { 30 | panic(err) 31 | } 32 | defer rtree.Close() 33 | 34 | if err = rtree.Add("217.72.192.0/20", "UDATA1"); err != nil { 35 | log.Fatal(err) 36 | } 37 | 38 | if err = rtree.Add("2001:220::/35", "UDATA2"); err != nil { 39 | log.Fatal(err) 40 | } 41 | 42 | var found bool 43 | var udata string 44 | 45 | found, udata, err = rtree.SearchBest("217.72.192.2") 46 | if err != nil { 47 | log.Fatal(err) 48 | } 49 | if found { 50 | fmt.Printf("found: %v\n", udata) 51 | } 52 | 53 | found, udata, err = rtree.SearchBest("2001:220::") 54 | if err != nil { 55 | log.Fatal(err) 56 | } 57 | if found { 58 | fmt.Printf("found: %v\n", udata) 59 | } 60 | 61 | if err = rtree.Remove("217.72.192.0/20"); err != nil { 62 | log.Fatal(err) 63 | } 64 | } 65 | 66 | ``` 67 | 68 | ## Documentation 69 | 70 | [![GoDoc](https://godoc.org/github.com/thekvs/go-net-radix?status.png)](https://godoc.org/github.com/thekvs/go-net-radix) 71 | 72 | ## Licensing 73 | 74 | All source code included in this distribution is covered by the MIT License found in the LICENSE file, 75 | unless specifically stated otherwise within each file. 76 | -------------------------------------------------------------------------------- /netradix.go: -------------------------------------------------------------------------------- 1 | package netradix 2 | 3 | /* 4 | #include "radix.h" 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | void 13 | free_node_payload(radix_node_t *node, void *cbctx) 14 | { 15 | free(node->data); 16 | } 17 | 18 | void 19 | destroy(radix_tree_t *tree) 20 | { 21 | Destroy_Radix(tree, free_node_payload, NULL); 22 | } 23 | 24 | radix_node_t* 25 | make_node(radix_tree_t *tree, char *addr, const char **errmsg) 26 | { 27 | radix_node_t *node; 28 | void *p; 29 | prefix_t prefix; 30 | 31 | p = prefix_pton(addr, -1, &prefix, errmsg); 32 | if (p == NULL) { 33 | return NULL; 34 | } 35 | 36 | node = radix_lookup(tree, &prefix); 37 | 38 | return node; 39 | } 40 | */ 41 | import "C" 42 | 43 | import ( 44 | "errors" 45 | "unsafe" 46 | ) 47 | 48 | type NetRadixTree struct { 49 | tree *C.radix_tree_t 50 | } 51 | 52 | // NewNetRadixTree creates new NetRadixTree structure. 53 | // Return tuple in which the first element specifies tree structure and the second 54 | // element specifies error object or nil if no error has occured. 55 | func NewNetRadixTree() (*NetRadixTree, error) { 56 | tree := &NetRadixTree{C.New_Radix()} 57 | if tree == nil { 58 | return nil, errors.New("couldn't create radix tree") 59 | } 60 | 61 | return tree, nil 62 | } 63 | 64 | // Close destroys radix tree. 65 | func (rtree *NetRadixTree) Close() { 66 | C.destroy(rtree.tree) 67 | } 68 | 69 | // Add adds network or subnet specification and user defined payload string to the radix tree. 70 | // If no mask width is specified, the longest possible mask is assumed, 71 | // i.e. 32 bits for IPv4 network and 128 bits for IPv6 network. 72 | // On success, returns nil, otherwise returns error object. 73 | func (rtree *NetRadixTree) Add(addr, udata string) error { 74 | cstr := C.CString(addr) 75 | defer C.free(unsafe.Pointer(cstr)) 76 | 77 | var errmsg *C.char 78 | node := C.make_node(rtree.tree, cstr, &errmsg) 79 | if node == nil { 80 | return errors.New(C.GoString(errmsg)) 81 | } 82 | 83 | node.data = unsafe.Pointer(C.CString(udata)) 84 | 85 | return nil 86 | } 87 | 88 | // SearchBest searches radix tree to find a matching node using usual subnetting rules 89 | // for the address specified. If no mask width is specified, the longest possible mask 90 | // for this type of address (IPv4 or IPv6) is assumed. 91 | // Returns triple in which the first element indicates success of a search, 92 | // the second element returns user payload (or empty string if not found) 93 | // and the third element returns error object in case such an error occured or nil otherwise. 94 | func (rtree *NetRadixTree) SearchBest(addr string) (found bool, udata string, err error) { 95 | var prefix C.prefix_t 96 | e := rtree.fillPrefix(&prefix, addr) 97 | if e != nil { 98 | return false, "", e 99 | } 100 | 101 | node := C.radix_search_best(rtree.tree, &prefix) 102 | if node != nil { 103 | return true, C.GoString((*C.char)(node.data)), nil 104 | } 105 | 106 | return false, "", nil 107 | } 108 | 109 | // SearchExact searches radix tree to find a matching node. Its semantics are the same as in SearchBest() 110 | // method except that the addr must match a node exactly. 111 | func (rtree *NetRadixTree) SearchExact(addr string) (found bool, udata string, err error) { 112 | var prefix C.prefix_t 113 | e := rtree.fillPrefix(&prefix, addr) 114 | if e != nil { 115 | return false, "", e 116 | } 117 | 118 | node := C.radix_search_exact(rtree.tree, &prefix) 119 | if node != nil { 120 | return true, C.GoString((*C.char)(node.data)), nil 121 | } 122 | 123 | return false, "", nil 124 | } 125 | 126 | // Remove deletes a node which exactly matches the address given. 127 | // If no errors occured returns nil or error object otherwise. 128 | func (rtree *NetRadixTree) Remove(addr string) error { 129 | var prefix C.prefix_t 130 | err := rtree.fillPrefix(&prefix, addr) 131 | if err != nil { 132 | return err 133 | } 134 | 135 | node := C.radix_search_exact(rtree.tree, &prefix) 136 | if node != nil { 137 | C.free(unsafe.Pointer(node.data)) 138 | C.radix_remove(rtree.tree, node) 139 | } 140 | 141 | return nil 142 | } 143 | 144 | func (rtree *NetRadixTree) fillPrefix(prefix *C.prefix_t, addr string) error { 145 | cstr := C.CString(addr) 146 | defer C.free(unsafe.Pointer(cstr)) 147 | 148 | var errmsg *C.char 149 | ptr := C.prefix_pton(cstr, -1, prefix, &errmsg) 150 | if ptr == nil { 151 | return errors.New(C.GoString(errmsg)) 152 | } 153 | 154 | return nil 155 | } 156 | -------------------------------------------------------------------------------- /netradix_test.go: -------------------------------------------------------------------------------- 1 | package netradix 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | type TestResult struct { 8 | ip string 9 | udata string 10 | result bool 11 | } 12 | 13 | type TestData struct { 14 | network string 15 | udata string 16 | } 17 | 18 | func TestSearchExact(t *testing.T) { 19 | rtree, err := NewNetRadixTree() 20 | if err != nil { 21 | t.Errorf("couldn't create structure") 22 | } 23 | defer rtree.Close() 24 | 25 | initial := []TestData{ 26 | {"217.72.192.0/20", "UDATA1"}, 27 | {"217.72.195.0/24", "UDATA2"}, 28 | {"195.161.113.74/32", "UDATA3"}, 29 | {"172.16.2.2", "UDATA4"}, 30 | {"10.42.0.0/16", "UDATA5"}, 31 | {"2001:220::/35", "UDATA6"}, 32 | } 33 | 34 | for _, value := range initial { 35 | if err := rtree.Add(value.network, value.udata); err != nil { 36 | t.Errorf("internal error %v", err) 37 | } 38 | } 39 | 40 | expected := []TestResult{ 41 | {"217.72.192.0/20", "UDATA1", true}, 42 | {"217.72.195.42", "", false}, 43 | {"195.161.113.74", "UDATA3", true}, 44 | {"172.16.2.2", "UDATA4", true}, 45 | {"15.161.13.75", "", false}, 46 | {"10.42.1.0/24", "", false}, 47 | {"10.42.1.8", "", false}, 48 | {"2001:220::/128", "", false}, 49 | {"2001:220::/35", "UDATA6", true}, 50 | } 51 | 52 | for _, value := range expected { 53 | status, udata, err := rtree.SearchExact(value.ip) 54 | if err != nil { 55 | t.Errorf("internal error %v", err) 56 | } 57 | if status != value.result { 58 | t.Errorf("unexpected result for ip %v", value.ip) 59 | } 60 | if status { 61 | if udata != value.udata { 62 | t.Errorf("unexpected result for ip %v: %v", value.ip, udata) 63 | } 64 | } 65 | } 66 | } 67 | 68 | func TestSearchBest(t *testing.T) { 69 | rtree, err := NewNetRadixTree() 70 | if err != nil { 71 | t.Errorf("couldn't create structure") 72 | } 73 | defer rtree.Close() 74 | 75 | initial := []TestData{ 76 | {"217.72.192.0/20", "UDATA1"}, 77 | {"217.72.195.0/24", "UDATA2"}, 78 | {"195.161.113.74/32", "UDATA3"}, 79 | {"172.16.2.2", "UDATA4"}, 80 | {"10.42.0.0/16", "UDATA5"}, 81 | {"2001:220::/35", "UDATA6"}, 82 | } 83 | 84 | expected := []TestResult{ 85 | {"217.72.192.1", "UDATA1", true}, 86 | {"217.72.195.42", "UDATA2", true}, 87 | {"195.161.113.74", "UDATA3", true}, 88 | {"172.16.2.2", "UDATA4", true}, 89 | {"15.161.13.75", "", false}, 90 | {"10.42.1.0/24", "UDATA5", true}, 91 | {"10.42.1.8", "UDATA5", true}, 92 | {"2001:220::/128", "UDATA6", true}, 93 | } 94 | 95 | for _, value := range initial { 96 | if err := rtree.Add(value.network, value.udata); err != nil { 97 | t.Errorf("internal error %v", err) 98 | } 99 | } 100 | 101 | for _, value := range expected { 102 | status, udata, err := rtree.SearchBest(value.ip) 103 | if err != nil { 104 | t.Errorf("internal error %v", err) 105 | } 106 | if status != value.result { 107 | t.Errorf("unexpected result for ip %v", value.ip) 108 | } 109 | if status { 110 | if udata != value.udata { 111 | t.Errorf("unexpected result for ip %v: %v", value.ip, udata) 112 | } 113 | } 114 | } 115 | } 116 | 117 | func TestRemove(t *testing.T) { 118 | rtree, err := NewNetRadixTree() 119 | if err != nil { 120 | t.Errorf("couldn't create structure") 121 | } 122 | defer rtree.Close() 123 | 124 | initial := []TestData{ 125 | {"217.72.192.0/20", "UDATA1"}, 126 | {"217.72.195.0/24", "UDATA2"}, 127 | {"195.161.113.74/32", "UDATA3"}, 128 | {"172.16.2.2", "UDATA4"}, 129 | {"10.42.0.0/16", "UDATA5"}, 130 | } 131 | 132 | expected := []TestResult{ 133 | {"217.72.192.0/20", "UDATA1", true}, 134 | {"195.161.113.74", "UDATA3", true}, 135 | {"172.16.2.2", "UDATA4", true}, 136 | } 137 | 138 | for _, value := range initial { 139 | if err := rtree.Add(value.network, value.udata); err != nil { 140 | t.Errorf("internal error %v", err) 141 | } 142 | } 143 | 144 | for _, value := range expected { 145 | status, udata, err := rtree.SearchExact(value.ip) 146 | if err != nil { 147 | t.Errorf("internal error %v", err) 148 | } 149 | if status != value.result { 150 | t.Errorf("unexpected result for ip %v", value.ip) 151 | } 152 | if status { 153 | if udata != value.udata { 154 | t.Errorf("unexpected result for ip %v: %v", value.ip, udata) 155 | } 156 | } 157 | } 158 | 159 | for _, value := range initial { 160 | rtree.Remove(value.network) 161 | } 162 | 163 | for _, value := range expected { 164 | status, _, err := rtree.SearchExact(value.ip) 165 | if err != nil { 166 | t.Errorf("internal error %v", err) 167 | } 168 | if status != (!value.result) { 169 | t.Errorf("unexpected result for ip %v", value.ip) 170 | } 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /radix.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 1999-2000 3 | * 4 | * The Regents of the University of Michigan ("The Regents") and 5 | * Merit Network, Inc. All rights reserved. Redistribution and use 6 | * in source and binary forms, with or without modification, are 7 | * permitted provided that the following conditions are met: 8 | * 9 | * 1. Redistributions of source code must retain the above 10 | * copyright notice, this list of conditions and the 11 | * following disclaimer. 12 | * 13 | * 2. Redistributions in binary form must reproduce the above 14 | * copyright notice, this list of conditions and the 15 | * following disclaimer in the documentation and/or other 16 | * materials provided with the distribution. 17 | * 18 | * 3. All advertising materials mentioning features or use of 19 | * this software must display the following acknowledgement: 20 | * 21 | * This product includes software developed by the University of 22 | * Michigan, Merit Network, Inc., and their contributors. 23 | * 24 | * 4. Neither the name of the University, Merit Network, nor the 25 | * names of their contributors may be used to endorse or 26 | * promote products derived from this software without 27 | * specific prior written permission. 28 | * 29 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" 30 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 31 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 32 | * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL TH E REGENTS 33 | * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 34 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 35 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 36 | * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HO WEVER CAUSED 37 | * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 38 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 39 | * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 40 | * POSSIBILITY OF SUCH DAMAGE. 41 | */ 42 | /* 43 | * Portions Copyright (c) 2004,2005 Damien Miller 44 | * 45 | * Permission to use, copy, modify, and distribute this software for any 46 | * purpose with or without fee is hereby granted, provided that the above 47 | * copyright notice and this permission notice appear in all copies. 48 | * 49 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 50 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 51 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 52 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 53 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 54 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 55 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 56 | */ 57 | 58 | /* $Id: radix.h,v 1.9 2007/10/24 06:03:08 djm Exp $ */ 59 | 60 | #ifndef _RADIX_H 61 | #define _RADIX_H 62 | 63 | #if defined(_MSC_VER) 64 | #include 65 | #include 66 | #else 67 | # include 68 | # include 69 | # include 70 | # include 71 | # include 72 | #endif 73 | 74 | #if defined(_MSC_VER) 75 | # define snprintf _snprintf 76 | typedef unsigned __int8 u_int8_t; 77 | typedef unsigned __int16 u_int16_t; 78 | typedef unsigned __int32 u_int32_t; 79 | const char *inet_ntop(int af, const void *src, char *dst, size_t size); 80 | size_t strlcpy(char *dst, const char *src, size_t size); 81 | #endif 82 | 83 | /* 84 | * Originally from MRT include/mrt.h 85 | * $MRTId: mrt.h,v 1.1.1.1 2000/08/14 18:46:10 labovit Exp $ 86 | */ 87 | typedef struct _prefix_t { 88 | u_int family; /* AF_INET | AF_INET6 */ 89 | u_int bitlen; /* same as mask? */ 90 | int ref_count; /* reference count */ 91 | union { 92 | struct in_addr sin; 93 | struct in6_addr sin6; 94 | } add; 95 | } prefix_t; 96 | 97 | void Deref_Prefix(prefix_t *prefix); 98 | 99 | /* 100 | * Originally from MRT include/radix.h 101 | * $MRTId: radix.h,v 1.1.1.1 2000/08/14 18:46:10 labovit Exp $ 102 | */ 103 | typedef struct _radix_node_t { 104 | u_int bit; /* flag if this node used */ 105 | prefix_t *prefix; /* who we are in radix tree */ 106 | struct _radix_node_t *l, *r; /* left and right children */ 107 | struct _radix_node_t *parent; /* may be used */ 108 | void *data; /* pointer to data */ 109 | } radix_node_t; 110 | 111 | typedef struct _radix_tree_t { 112 | radix_node_t *head; 113 | u_int maxbits; /* for IP, 32 bit addresses */ 114 | int num_active_node; /* for debug purpose */ 115 | } radix_tree_t; 116 | 117 | /* Type of callback function */ 118 | typedef void (*rdx_cb_t)(radix_node_t *, void *); 119 | 120 | radix_tree_t *New_Radix(void); 121 | void Destroy_Radix(radix_tree_t *radix, rdx_cb_t func, void *cbctx); 122 | radix_node_t *radix_lookup(radix_tree_t *radix, prefix_t *prefix); 123 | void radix_remove(radix_tree_t *radix, radix_node_t *node); 124 | radix_node_t *radix_search_exact(radix_tree_t *radix, prefix_t *prefix); 125 | radix_node_t *radix_search_best(radix_tree_t *radix, prefix_t *prefix); 126 | void radix_process(radix_tree_t *radix, rdx_cb_t func, void *cbctx); 127 | 128 | #define RADIX_MAXBITS 128 129 | 130 | #define RADIX_WALK(Xhead, Xnode) \ 131 | do { \ 132 | radix_node_t *Xstack[RADIX_MAXBITS+1]; \ 133 | radix_node_t **Xsp = Xstack; \ 134 | radix_node_t *Xrn = (Xhead); \ 135 | while ((Xnode = Xrn)) { \ 136 | if (Xnode->prefix) 137 | 138 | #define RADIX_WALK_END \ 139 | if (Xrn->l) { \ 140 | if (Xrn->r) { \ 141 | *Xsp++ = Xrn->r; \ 142 | } \ 143 | Xrn = Xrn->l; \ 144 | } else if (Xrn->r) { \ 145 | Xrn = Xrn->r; \ 146 | } else if (Xsp != Xstack) { \ 147 | Xrn = *(--Xsp); \ 148 | } else { \ 149 | Xrn = (radix_node_t *) 0; \ 150 | } \ 151 | } \ 152 | } while (0) 153 | 154 | /* Local additions */ 155 | 156 | prefix_t *prefix_pton(const char *string, long len, prefix_t *prefix, const char **errmsg); 157 | prefix_t *prefix_from_blob(u_char *blob, int len, int prefixlen); 158 | const char *prefix_addr_ntop(prefix_t *prefix, char *buf, size_t len); 159 | const char *prefix_ntop(prefix_t *prefix, char *buf, size_t len); 160 | 161 | #endif /* _RADIX_H */ 162 | -------------------------------------------------------------------------------- /radix.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 1999-2000 3 | * 4 | * The Regents of the University of Michigan ("The Regents") and 5 | * Merit Network, Inc. All rights reserved. Redistribution and use 6 | * in source and binary forms, with or without modification, are 7 | * permitted provided that the following conditions are met: 8 | * 9 | * 1. Redistributions of source code must retain the above 10 | * copyright notice, this list of conditions and the 11 | * following disclaimer. 12 | * 13 | * 2. Redistributions in binary form must reproduce the above 14 | * copyright notice, this list of conditions and the 15 | * following disclaimer in the documentation and/or other 16 | * materials provided with the distribution. 17 | * 18 | * 3. All advertising materials mentioning features or use of 19 | * this software must display the following acknowledgement: 20 | * 21 | * This product includes software developed by the University of 22 | * Michigan, Merit Network, Inc., and their contributors. 23 | * 24 | * 4. Neither the name of the University, Merit Network, nor the 25 | * names of their contributors may be used to endorse or 26 | * promote products derived from this software without 27 | * specific prior written permission. 28 | * 29 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" 30 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 31 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 32 | * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL TH E REGENTS 33 | * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 34 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 35 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 36 | * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HO WEVER CAUSED 37 | * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 38 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 39 | * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 40 | * POSSIBILITY OF SUCH DAMAGE. 41 | */ 42 | /* 43 | * Portions Copyright (c) 2004,2005 Damien Miller 44 | * 45 | * Permission to use, copy, modify, and distribute this software for any 46 | * purpose with or without fee is hereby granted, provided that the above 47 | * copyright notice and this permission notice appear in all copies. 48 | * 49 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 50 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 51 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 52 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 53 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 54 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 55 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 56 | */ 57 | 58 | #include 59 | #include 60 | #include 61 | #include 62 | 63 | #include "radix.h" 64 | 65 | /* $Id: radix.c,v 1.17 2007/10/24 06:04:31 djm Exp $ */ 66 | 67 | /* 68 | * Originally from MRT include/defs.h 69 | * $MRTId: defs.h,v 1.1.1.1 2000/08/14 18:46:10 labovit Exp $ 70 | */ 71 | #define BIT_TEST(f, b) ((f) & (b)) 72 | 73 | /* 74 | * Originally from MRT include/mrt.h 75 | * $MRTId: mrt.h,v 1.1.1.1 2000/08/14 18:46:10 labovit Exp $ 76 | */ 77 | #define prefix_tochar(prefix) ((char *)&(prefix)->add) 78 | #define prefix_touchar(prefix) ((u_char *)&(prefix)->add) 79 | 80 | /* 81 | * Originally from MRT lib/mrt/prefix.c 82 | * $MRTId: prefix.c,v 1.1.1.1 2000/08/14 18:46:11 labovit Exp $ 83 | */ 84 | 85 | static int 86 | comp_with_mask(u_char *addr, u_char *dest, u_int mask) 87 | { 88 | if (memcmp(addr, dest, mask / 8) == 0) { 89 | u_int n = mask / 8; 90 | u_int m = ((~0) << (8 - (mask % 8))); 91 | 92 | if (mask % 8 == 0 || (addr[n] & m) == (dest[n] & m)) 93 | return (1); 94 | } 95 | return (0); 96 | } 97 | 98 | static prefix_t 99 | *New_Prefix2(int family, void *dest, int bitlen, prefix_t *prefix) 100 | { 101 | int dynamic_allocated = 0; 102 | int default_bitlen = 32; 103 | 104 | if (family == AF_INET6) { 105 | default_bitlen = 128; 106 | if (prefix == NULL) { 107 | if ((prefix = malloc(sizeof(*prefix))) == NULL) 108 | return (NULL); 109 | memset(prefix, '\0', sizeof(*prefix)); 110 | dynamic_allocated++; 111 | } 112 | memcpy(&prefix->add.sin6, dest, 16); 113 | } else if (family == AF_INET) { 114 | if (prefix == NULL) { 115 | if ((prefix = malloc(sizeof(*prefix))) == NULL) 116 | return (NULL); 117 | memset(prefix, '\0', sizeof(*prefix)); 118 | dynamic_allocated++; 119 | } 120 | memcpy(&prefix->add.sin, dest, 4); 121 | } else 122 | return (NULL); 123 | 124 | prefix->bitlen = (bitlen >= 0) ? bitlen : default_bitlen; 125 | prefix->family = family; 126 | prefix->ref_count = 0; 127 | if (dynamic_allocated) 128 | prefix->ref_count++; 129 | return (prefix); 130 | } 131 | 132 | 133 | static prefix_t 134 | *Ref_Prefix(prefix_t *prefix) 135 | { 136 | if (prefix == NULL) 137 | return (NULL); 138 | if (prefix->ref_count == 0) { 139 | /* make a copy in case of a static prefix */ 140 | return (New_Prefix2(prefix->family, &prefix->add, 141 | prefix->bitlen, NULL)); 142 | } 143 | prefix->ref_count++; 144 | return (prefix); 145 | } 146 | 147 | 148 | void 149 | Deref_Prefix(prefix_t *prefix) 150 | { 151 | if (prefix == NULL) 152 | return; 153 | prefix->ref_count--; 154 | if (prefix->ref_count <= 0) { 155 | free(prefix); 156 | return; 157 | } 158 | } 159 | 160 | /* 161 | * Originally from MRT lib/radix/radix.c 162 | * $MRTId: radix.c,v 1.1.1.1 2000/08/14 18:46:13 labovit Exp $ 163 | */ 164 | 165 | /* these routines support continuous mask only */ 166 | 167 | radix_tree_t 168 | *New_Radix(void) 169 | { 170 | radix_tree_t *radix; 171 | 172 | if ((radix = malloc(sizeof(*radix))) == NULL) 173 | return (NULL); 174 | memset(radix, '\0', sizeof(*radix)); 175 | 176 | radix->maxbits = 128; 177 | radix->head = NULL; 178 | radix->num_active_node = 0; 179 | return (radix); 180 | } 181 | 182 | /* 183 | * if func is supplied, it will be called as func(node->data) 184 | * before deleting the node 185 | */ 186 | static void 187 | Clear_Radix(radix_tree_t *radix, rdx_cb_t func, void *cbctx) 188 | { 189 | if (radix->head) { 190 | radix_node_t *Xstack[RADIX_MAXBITS + 1]; 191 | radix_node_t **Xsp = Xstack; 192 | radix_node_t *Xrn = radix->head; 193 | 194 | while (Xrn) { 195 | radix_node_t *l = Xrn->l; 196 | radix_node_t *r = Xrn->r; 197 | 198 | if (Xrn->prefix) { 199 | Deref_Prefix(Xrn->prefix); 200 | if (Xrn->data && func) 201 | func(Xrn, cbctx); 202 | } 203 | free(Xrn); 204 | radix->num_active_node--; 205 | 206 | if (l) { 207 | if (r) 208 | *Xsp++ = r; 209 | Xrn = l; 210 | } else if (r) { 211 | Xrn = r; 212 | } else if (Xsp != Xstack) { 213 | Xrn = *(--Xsp); 214 | } else { 215 | Xrn = (radix_node_t *) 0; 216 | } 217 | } 218 | } 219 | } 220 | 221 | void 222 | Destroy_Radix(radix_tree_t *radix, rdx_cb_t func, void *cbctx) 223 | { 224 | Clear_Radix(radix, func, cbctx); 225 | free(radix); 226 | } 227 | 228 | /* 229 | * if func is supplied, it will be called as func(node->prefix, node->data) 230 | */ 231 | void 232 | radix_process(radix_tree_t *radix, rdx_cb_t func, void *cbctx) 233 | { 234 | radix_node_t *node; 235 | 236 | RADIX_WALK(radix->head, node) { 237 | func(node, cbctx); 238 | } RADIX_WALK_END; 239 | } 240 | 241 | radix_node_t 242 | *radix_search_exact(radix_tree_t *radix, prefix_t *prefix) 243 | { 244 | radix_node_t *node; 245 | u_char *addr; 246 | u_int bitlen; 247 | 248 | if (radix->head == NULL) 249 | return (NULL); 250 | 251 | node = radix->head; 252 | addr = prefix_touchar(prefix); 253 | bitlen = prefix->bitlen; 254 | 255 | while (node->bit < bitlen) { 256 | if (BIT_TEST(addr[node->bit >> 3], 0x80 >> (node->bit & 0x07))) 257 | node = node->r; 258 | else 259 | node = node->l; 260 | 261 | if (node == NULL) 262 | return (NULL); 263 | } 264 | 265 | if (node->bit > bitlen || node->prefix == NULL) 266 | return (NULL); 267 | 268 | if (comp_with_mask(prefix_tochar(node->prefix), 269 | prefix_tochar(prefix), bitlen)) 270 | return (node); 271 | 272 | return (NULL); 273 | } 274 | 275 | 276 | /* if inclusive != 0, "best" may be the given prefix itself */ 277 | static radix_node_t 278 | *radix_search_best2(radix_tree_t *radix, prefix_t *prefix, int inclusive) 279 | { 280 | radix_node_t *node; 281 | radix_node_t *stack[RADIX_MAXBITS + 1]; 282 | u_char *addr; 283 | u_int bitlen; 284 | int cnt = 0; 285 | 286 | if (radix->head == NULL) 287 | return (NULL); 288 | 289 | node = radix->head; 290 | addr = prefix_touchar(prefix); 291 | bitlen = prefix->bitlen; 292 | 293 | while (node->bit < bitlen) { 294 | if (node->prefix) 295 | stack[cnt++] = node; 296 | if (BIT_TEST(addr[node->bit >> 3], 0x80 >> (node->bit & 0x07))) 297 | node = node->r; 298 | else 299 | node = node->l; 300 | 301 | if (node == NULL) 302 | break; 303 | } 304 | 305 | if (inclusive && node && node->prefix) 306 | stack[cnt++] = node; 307 | 308 | 309 | if (cnt <= 0) 310 | return (NULL); 311 | 312 | while (--cnt >= 0) { 313 | node = stack[cnt]; 314 | if (comp_with_mask(prefix_tochar(node->prefix), 315 | prefix_tochar(prefix), node->prefix->bitlen)) 316 | return (node); 317 | } 318 | return (NULL); 319 | } 320 | 321 | 322 | radix_node_t 323 | *radix_search_best(radix_tree_t *radix, prefix_t *prefix) 324 | { 325 | return (radix_search_best2(radix, prefix, 1)); 326 | } 327 | 328 | 329 | radix_node_t 330 | *radix_lookup(radix_tree_t *radix, prefix_t *prefix) 331 | { 332 | radix_node_t *node, *new_node, *parent, *glue; 333 | u_char *addr, *test_addr; 334 | u_int bitlen, check_bit, differ_bit; 335 | u_int i, j, r; 336 | 337 | if (radix->head == NULL) { 338 | if ((node = malloc(sizeof(*node))) == NULL) 339 | return (NULL); 340 | memset(node, '\0', sizeof(*node)); 341 | node->bit = prefix->bitlen; 342 | node->prefix = Ref_Prefix(prefix); 343 | node->parent = NULL; 344 | node->l = node->r = NULL; 345 | node->data = NULL; 346 | radix->head = node; 347 | radix->num_active_node++; 348 | return (node); 349 | } 350 | addr = prefix_touchar(prefix); 351 | bitlen = prefix->bitlen; 352 | node = radix->head; 353 | 354 | while (node->bit < bitlen || node->prefix == NULL) { 355 | if (node->bit < radix->maxbits && BIT_TEST(addr[node->bit >> 3], 356 | 0x80 >> (node->bit & 0x07))) { 357 | if (node->r == NULL) 358 | break; 359 | node = node->r; 360 | } else { 361 | if (node->l == NULL) 362 | break; 363 | node = node->l; 364 | } 365 | } 366 | 367 | test_addr = prefix_touchar(node->prefix); 368 | /* find the first bit different */ 369 | check_bit = (node->bit < bitlen) ? node->bit : bitlen; 370 | differ_bit = 0; 371 | for (i = 0; i * 8 < check_bit; i++) { 372 | if ((r = (addr[i] ^ test_addr[i])) == 0) { 373 | differ_bit = (i + 1) * 8; 374 | continue; 375 | } 376 | /* I know the better way, but for now */ 377 | for (j = 0; j < 8; j++) { 378 | if (BIT_TEST(r, (0x80 >> j))) 379 | break; 380 | } 381 | /* must be found */ 382 | differ_bit = i * 8 + j; 383 | break; 384 | } 385 | if (differ_bit > check_bit) 386 | differ_bit = check_bit; 387 | 388 | parent = node->parent; 389 | while (parent && parent->bit >= differ_bit) { 390 | node = parent; 391 | parent = node->parent; 392 | } 393 | 394 | if (differ_bit == bitlen && node->bit == bitlen) { 395 | if (node->prefix == NULL) 396 | node->prefix = Ref_Prefix(prefix); 397 | return (node); 398 | } 399 | if ((new_node = malloc(sizeof(*new_node))) == NULL) 400 | return (NULL); 401 | memset(new_node, '\0', sizeof(*new_node)); 402 | new_node->bit = prefix->bitlen; 403 | new_node->prefix = Ref_Prefix(prefix); 404 | new_node->parent = NULL; 405 | new_node->l = new_node->r = NULL; 406 | new_node->data = NULL; 407 | radix->num_active_node++; 408 | 409 | if (node->bit == differ_bit) { 410 | new_node->parent = node; 411 | if (node->bit < radix->maxbits && BIT_TEST(addr[node->bit >> 3], 412 | 0x80 >> (node->bit & 0x07))) 413 | node->r = new_node; 414 | else 415 | node->l = new_node; 416 | 417 | return (new_node); 418 | } 419 | if (bitlen == differ_bit) { 420 | if (bitlen < radix->maxbits && BIT_TEST(test_addr[bitlen >> 3], 421 | 0x80 >> (bitlen & 0x07))) 422 | new_node->r = node; 423 | else 424 | new_node->l = node; 425 | 426 | new_node->parent = node->parent; 427 | if (node->parent == NULL) 428 | radix->head = new_node; 429 | else if (node->parent->r == node) 430 | node->parent->r = new_node; 431 | else 432 | node->parent->l = new_node; 433 | 434 | node->parent = new_node; 435 | } else { 436 | if ((glue = malloc(sizeof(*glue))) == NULL) 437 | return (NULL); 438 | memset(glue, '\0', sizeof(*glue)); 439 | glue->bit = differ_bit; 440 | glue->prefix = NULL; 441 | glue->parent = node->parent; 442 | glue->data = NULL; 443 | radix->num_active_node++; 444 | if (differ_bit < radix->maxbits && 445 | BIT_TEST(addr[differ_bit >> 3], 446 | 0x80 >> (differ_bit & 0x07))) { 447 | glue->r = new_node; 448 | glue->l = node; 449 | } else { 450 | glue->r = node; 451 | glue->l = new_node; 452 | } 453 | new_node->parent = glue; 454 | 455 | if (node->parent == NULL) 456 | radix->head = glue; 457 | else if (node->parent->r == node) 458 | node->parent->r = glue; 459 | else 460 | node->parent->l = glue; 461 | 462 | node->parent = glue; 463 | } 464 | return (new_node); 465 | } 466 | 467 | 468 | void 469 | radix_remove(radix_tree_t *radix, radix_node_t *node) 470 | { 471 | radix_node_t *parent, *child; 472 | 473 | if (node->r && node->l) { 474 | /* 475 | * this might be a placeholder node -- have to check and make 476 | * sure there is a prefix aossciated with it ! 477 | */ 478 | if (node->prefix != NULL) 479 | Deref_Prefix(node->prefix); 480 | node->prefix = NULL; 481 | /* Also I needed to clear data pointer -- masaki */ 482 | node->data = NULL; 483 | return; 484 | } 485 | if (node->r == NULL && node->l == NULL) { 486 | parent = node->parent; 487 | Deref_Prefix(node->prefix); 488 | free(node); 489 | radix->num_active_node--; 490 | 491 | if (parent == NULL) { 492 | radix->head = NULL; 493 | return; 494 | } 495 | if (parent->r == node) { 496 | parent->r = NULL; 497 | child = parent->l; 498 | } else { 499 | parent->l = NULL; 500 | child = parent->r; 501 | } 502 | 503 | if (parent->prefix) 504 | return; 505 | 506 | /* we need to remove parent too */ 507 | if (parent->parent == NULL) 508 | radix->head = child; 509 | else if (parent->parent->r == parent) 510 | parent->parent->r = child; 511 | else 512 | parent->parent->l = child; 513 | 514 | child->parent = parent->parent; 515 | free(parent); 516 | radix->num_active_node--; 517 | return; 518 | } 519 | if (node->r) 520 | child = node->r; 521 | else 522 | child = node->l; 523 | 524 | parent = node->parent; 525 | child->parent = parent; 526 | 527 | Deref_Prefix(node->prefix); 528 | free(node); 529 | radix->num_active_node--; 530 | 531 | if (parent == NULL) { 532 | radix->head = child; 533 | return; 534 | } 535 | if (parent->r == node) 536 | parent->r = child; 537 | else 538 | parent->l = child; 539 | } 540 | 541 | /* Local additions */ 542 | static void 543 | sanitise_mask(u_char *addr, u_int masklen, u_int maskbits) 544 | { 545 | u_int i = masklen / 8; 546 | u_int j = masklen % 8; 547 | 548 | if (j != 0) { 549 | addr[i] &= (~0) << (8 - j); 550 | i++; 551 | } 552 | for (; i < maskbits / 8; i++) 553 | addr[i] = 0; 554 | } 555 | 556 | prefix_t 557 | *prefix_pton(const char *string, long len, prefix_t *prefix, const char **errmsg) 558 | { 559 | char save[256], *cp, *ep; 560 | struct addrinfo hints, *ai; 561 | void *addr; 562 | prefix_t *ret; 563 | size_t slen; 564 | int r; 565 | 566 | ret = NULL; 567 | 568 | /* Copy the string to parse, because we modify it */ 569 | if ((slen = strlen(string) + 1) > sizeof(save)) { 570 | *errmsg = "string too long"; 571 | return (NULL); 572 | } 573 | memcpy(save, string, slen); 574 | 575 | if ((cp = strchr(save, '/')) != NULL) { 576 | if (len != -1 ) { 577 | *errmsg = "masklen specified twice"; 578 | return (NULL); 579 | } 580 | *cp++ = '\0'; 581 | len = strtol(cp, &ep, 10); 582 | if (*cp == '\0' || *ep != '\0' || len < 0) { 583 | *errmsg = "could not parse masklen"; 584 | return (NULL); 585 | } 586 | /* More checks below */ 587 | } 588 | memset(&hints, '\0', sizeof(hints)); 589 | hints.ai_flags = AI_NUMERICHOST; 590 | 591 | if ((r = getaddrinfo(save, NULL, &hints, &ai)) != 0) { 592 | snprintf(save, sizeof(save), "getaddrinfo: %s:", 593 | gai_strerror(r)); 594 | *errmsg = save; 595 | return NULL; 596 | } 597 | if (ai == NULL || ai->ai_addr == NULL) { 598 | *errmsg = "getaddrinfo returned no result"; 599 | goto out; 600 | } 601 | switch (ai->ai_addr->sa_family) { 602 | case AF_INET: 603 | if (len == -1) 604 | len = 32; 605 | else if (len < 0 || len > 32) 606 | goto out; 607 | addr = &((struct sockaddr_in *) ai->ai_addr)->sin_addr; 608 | sanitise_mask(addr, len, 32); 609 | break; 610 | case AF_INET6: 611 | if (len == -1) 612 | len = 128; 613 | else if (len < 0 || len > 128) 614 | goto out; 615 | addr = &((struct sockaddr_in6 *) ai->ai_addr)->sin6_addr; 616 | sanitise_mask(addr, len, 128); 617 | break; 618 | default: 619 | goto out; 620 | } 621 | 622 | ret = New_Prefix2(ai->ai_addr->sa_family, addr, len, prefix); 623 | if (ret == NULL) 624 | *errmsg = "New_Prefix2 failed"; 625 | out: 626 | freeaddrinfo(ai); 627 | return (ret); 628 | } 629 | 630 | prefix_t 631 | *prefix_from_blob(u_char *blob, int len, int prefixlen) 632 | { 633 | int family, maxprefix; 634 | 635 | switch (len) { 636 | case 4: 637 | /* Assume AF_INET */ 638 | family = AF_INET; 639 | maxprefix = 32; 640 | break; 641 | case 16: 642 | /* Assume AF_INET6 */ 643 | family = AF_INET6; 644 | maxprefix = 128; 645 | break; 646 | default: 647 | /* Who knows? */ 648 | return NULL; 649 | } 650 | if (prefixlen == -1) 651 | prefixlen = maxprefix; 652 | if (prefixlen < 0 || prefixlen > maxprefix) 653 | return NULL; 654 | return (New_Prefix2(family, blob, prefixlen, NULL)); 655 | } 656 | 657 | const char * 658 | prefix_addr_ntop(prefix_t *prefix, char *buf, size_t len) 659 | { 660 | return (inet_ntop(prefix->family, &prefix->add, buf, len)); 661 | } 662 | 663 | const char * 664 | prefix_ntop(prefix_t *prefix, char *buf, size_t len) 665 | { 666 | char addrbuf[128]; 667 | 668 | if (prefix_addr_ntop(prefix, addrbuf, sizeof(addrbuf)) == NULL) 669 | return (NULL); 670 | snprintf(buf, len, "%s/%d", addrbuf, prefix->bitlen); 671 | 672 | return (buf); 673 | } 674 | --------------------------------------------------------------------------------