1 /* Copyright 2008 Bernhard R. Fischer, Daniel Haslinger.
2  *
3  * This file is part of OnionCat.
4  *
5  * OnionCat is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, version 3 of the License.
8  *
9  * OnionCat is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with OnionCat. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 /*! ocatipv4route.c
19  *  This file contains functions for managing IPv4 routing and
20  *  forwarding.
21  *
22  *  @author Bernhard R. Fischer <rahra _at_ cypherpunk at>
23  *  @version 2008/09/03-01
24  */
25 
26 
27 #include "ocat.h"
28 
29 #define NMBIT(i,n) ((i&n)&~(n<<1))
30 #define BRANCH(i,n) (NMBIT(i,n)!=0)
31 
32 
33 static IPv4Route_t *rroot_ = NULL;
34 static pthread_mutex_t route_mutex_ = PTHREAD_MUTEX_INITIALIZER;
35 
36 
37 /*! Add an IPv4 route to IPv4 routing table.
38  *  @return 0 on success or < 0 on failure.
39  */
ipv4_add_route(IPv4Route_t * route,IPv4Route_t ** root,uint32_t cur_nm)40 int ipv4_add_route(IPv4Route_t *route, IPv4Route_t **root, uint32_t cur_nm)
41 {
42    if (!(*root))
43    {
44       if (!(*root = calloc(1, sizeof(IPv4Route_t))))
45       {
46          log_msg(LOG_EMERG, "ipv4_add_route: %s", strerror(errno));
47          return E_RT_NOMEM;
48       }
49       (*root)->dest = route->dest & cur_nm;
50       (*root)->netmask = cur_nm;
51    }
52 
53    if (route->netmask == cur_nm /*(*root)->netmask*/)
54    {
55       if (IN6_ARE_ADDR_EQUAL(&(*root)->gw, &in6addr_any))
56       {
57          memcpy(&(*root)->gw, &route->gw, sizeof(struct in6_addr));
58          return 0;
59       }
60 
61       if (IN6_ARE_ADDR_EQUAL(&(*root)->gw, &route->gw))
62          return 0;
63 
64       log_msg(LOG_ERR, "route already exists");
65       return E_RT_DUP;
66    }
67 
68    // break recursion in case of error
69    if (cur_nm == 0xffffffff)
70    {
71       log_msg(LOG_ERR, "netmask error in netmask of route: %08x", route->netmask);
72       return E_RT_ILLNM;
73    }
74 
75    //now branch to subs
76    cur_nm >>= 1;
77    cur_nm |= 0x80000000;
78 
79    return ipv4_add_route(route, &(*root)->next[BRANCH(route->dest, cur_nm)], cur_nm);
80 }
81 
82 
ipv4_lookup_route__(uint32_t ip,IPv4Route_t * route,uint32_t cur_nm)83 IPv4Route_t *ipv4_lookup_route__(uint32_t ip, IPv4Route_t *route, uint32_t cur_nm)
84 {
85    if (!route)
86    {
87       log_debug("NULL route");
88       return NULL;
89    }
90 
91    cur_nm >>= 1;
92    cur_nm |= 0x80000000;
93 
94    if (route->next[BRANCH(ip, cur_nm)])
95       return ipv4_lookup_route__(ip, route->next[BRANCH(ip, cur_nm)], cur_nm);
96 
97    //if (memcmp(&route->gw, &in6addr_any, sizeof(struct in6_addr)))
98    if (!IN6_ARE_ADDR_EQUAL(&route->gw, &in6addr_any))
99       return route;
100 
101    return NULL;
102 }
103 
104 
105 /*! Lookup a route to an ip address in routing table.
106  *  @param Ip to find a route for. The Ip must be given in host byte order.
107  *  @return Pointer to IPv6 TOR address. */
ipv4_lookup_route(uint32_t ip)108 struct in6_addr *ipv4_lookup_route(uint32_t ip)
109 {
110    IPv4Route_t *r;
111 
112    pthread_mutex_lock(&route_mutex_);
113    r = ipv4_lookup_route__(ip, rroot_, 0);
114    pthread_mutex_unlock(&route_mutex_);
115 
116    return r ? &r->gw : NULL;
117 }
118 
119 
ipv4_traverse(IPv4Route_t * route,void (func)(IPv4Route_t *,void *),void * p)120 void ipv4_traverse(IPv4Route_t *route, void (func)(IPv4Route_t*, void*), void *p)
121 {
122    if (!route)
123       return;
124 
125    func(route, p);
126    ipv4_traverse(route->next[0], func, p);
127    ipv4_traverse(route->next[1], func, p);
128 }
129 
130 
ipv4_print(IPv4Route_t * route,void * f)131 void ipv4_print(IPv4Route_t *route, void *f)
132 {
133    char addr[INET6_ADDRSTRLEN];
134    struct in_addr iaddr;
135 
136    //if (!memcmp(&route->gw, &in6addr_any, sizeof(struct in6_addr)))
137    if (IN6_ARE_ADDR_EQUAL(&route->gw, &in6addr_any))
138       return;
139 
140    iaddr.s_addr = htonl(route->dest);
141    fprintf(f, "IN  %s ", inet_ntoa(iaddr));
142    iaddr.s_addr = htonl(route->netmask);
143    fprintf(f, "%s ", inet_ntoa(iaddr));
144    inet_ntop(AF_INET6, &route->gw, addr, INET6_ADDRSTRLEN);
145    fprintf(f, "%s %p\n", addr, route);
146 }
147 
148 
print_routes(FILE * f)149 void print_routes(FILE *f)
150 {
151    ipv4_traverse(rroot_, ipv4_print, f);
152 }
153 
154 
parse_route(const char * rs)155 int parse_route(const char *rs)
156 {
157    char buf[strlen(rs) + 1], *s, *b;
158    IPv4Route_t route;
159    int r;
160 
161    if (!rs)
162       return E_RT_NULLPTR;
163 
164    log_debug("IPv4 route parser: \"%s\"", rs);
165 
166    strlcpy(buf, rs, strlen(rs) + 1);
167    if (!(s = strtok_r(buf, " \t", &b)))
168       return E_RT_SYNTAX;
169 
170    if (inet_pton(AF_INET, s, &route.dest) != 1)
171       return E_RT_SYNTAX;
172 
173    if (!(s = strtok_r(NULL, " \t", &b)))
174       return E_RT_SYNTAX;
175 
176    if (inet_pton(AF_INET, s, &route.netmask) != 1)
177       return E_RT_SYNTAX;
178 
179    if (!(s = strtok_r(NULL, " \t", &b)))
180       return E_RT_SYNTAX;
181 
182    if (inet_pton(AF_INET6, s, &route.gw) != 1)
183       return E_RT_SYNTAX;
184 
185    if (!has_tor_prefix(&route.gw))
186       return E_RT_NOTORGW;
187 
188    if (IN6_ARE_ADDR_EQUAL(&route.gw, &CNF(ocat_addr)))
189       return E_RT_GWSELF;
190 
191    route.netmask = ntohl(route.netmask);
192    route.dest = ntohl(route.dest);
193 
194    pthread_mutex_lock(&route_mutex_);
195    r = ipv4_add_route(&route, &rroot_, 0);
196    pthread_mutex_unlock(&route_mutex_);
197 
198    return r;
199 }
200 
201