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 /*! @file
19  *  This file contains functions for managing IPv6 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 
30 /*! IPv6 Routing table. Each entry contains 3 values:
31  *  destination network, prefix length, gateway
32  */
33 static IPv6Route_t *v6route_ = NULL;
34 static int v6route_cnt_ = 0;
35 static pthread_mutex_t v6route_mutex_ = PTHREAD_MUTEX_INITIALIZER;
36 
37 
38 /*! Reduce IPv6 address to prefix, i.e. cut off host id.
39  *  @param net IPv6 address
40  *  @param prefixlen Prefix length
41  */
ipv6_reduce(struct in6_addr * net,int prefixlen)42 void ipv6_reduce(struct in6_addr *net, int prefixlen)
43 {
44    int i;
45    char m;
46 
47    // safety check
48    if (prefixlen < 0 || prefixlen >= 128)
49       return;
50 
51    for (i = 0; i < ((128 - prefixlen) >> 3); i++)
52       net->s6_addr[15 - i] = 0;
53 
54    m = 0xff << (8 - (prefixlen % 8));
55    net->s6_addr[prefixlen >> 3] &= m;
56 
57 }
58 
59 
60 /*! Lookup IPv6 route.
61  */
ipv6_lookup_route(const struct in6_addr * dest)62 struct in6_addr *ipv6_lookup_route(const struct in6_addr *dest)
63 {
64    struct in6_addr addr;
65    int i, n;
66 
67    pthread_mutex_lock(&v6route_mutex_);
68    n = v6route_cnt_;
69    for (i = 0; i < n; i++)
70    {
71       addr = *dest;
72       ipv6_reduce(&addr, v6route_[i].prefixlen);
73       if (IN6_ARE_ADDR_EQUAL(&v6route_[i].dest, &addr))
74       {
75          log_debug("IPv6 route found");
76          break;
77       }
78    }
79    pthread_mutex_unlock(&v6route_mutex_);
80    return i < n ? &v6route_[i].gw : NULL;
81 }
82 
83 
84 /*! Add an IPv6 route to IPv6 routing table.
85  *  @return -1 if table is full else return index of entry.
86  */
ipv6_add_route(const IPv6Route_t * route)87 int ipv6_add_route(const IPv6Route_t *route)
88 {
89    int r = -1;
90    IPv6Route_t *rt;
91 
92    pthread_mutex_lock(&v6route_mutex_);
93    if ((rt = realloc(v6route_, sizeof(IPv6Route_t) * (v6route_cnt_ + 1))))
94    {
95       v6route_ = rt;
96       r = v6route_cnt_;
97       memcpy(&v6route_[v6route_cnt_++], route, sizeof(IPv6Route_t));
98    }
99    pthread_mutex_unlock(&v6route_mutex_);
100    return r;
101 }
102 
103 
ipv6_print(IPv6Route_t * route,void * f)104 void ipv6_print(IPv6Route_t *route, void *f)
105 {
106    char addr[INET6_ADDRSTRLEN];
107 
108    inet_ntop(AF_INET6, &route->dest, addr, INET6_ADDRSTRLEN);
109    fprintf(f, "IN6 %s %3d ", addr, route->prefixlen);
110    inet_ntop(AF_INET6, &route->gw, addr, INET6_ADDRSTRLEN);
111    fprintf(f, "%s %p\n", addr, route);
112 }
113 
114 
ipv6_print_routes(FILE * f)115 void ipv6_print_routes(FILE *f)
116 {
117    int i;
118 
119    pthread_mutex_lock(&v6route_mutex_);
120    for (i = 0; i < v6route_cnt_; i++)
121       ipv6_print(&v6route_[i], f);
122    pthread_mutex_unlock(&v6route_mutex_);
123 }
124 
125 
126 /*! Parse IPv6 route and add it to routing table.
127  *  @return index of routing table entry (>= 0) or an integer < 0 on failure.
128  */
ipv6_parse_route(const char * rs)129 int ipv6_parse_route(const char *rs)
130 {
131    char buf[strlen(rs) + 1], *s, *b;
132    IPv6Route_t route6;
133 
134    if (!rs)
135       return E_RT_NULLPTR;
136 
137    log_debug("IPv6 route parser: \"%s\"", rs);
138 
139    strlcpy(buf, rs, strlen(rs) + 1);
140    if (!(s = strtok_r(buf, " \t", &b)))
141       return E_RT_SYNTAX;
142 
143    if (inet_pton(AF_INET6, s, &route6.dest) != 1)
144       return E_RT_SYNTAX;
145 
146    if (!(s = strtok_r(NULL, " \t", &b)))
147       return E_RT_SYNTAX;
148 
149    errno = 0;
150    route6.prefixlen = strtol(s, NULL, 10);
151    if (errno)
152       return E_RT_SYNTAX;
153    if ((route6.prefixlen < 0) || (route6.prefixlen > 128))
154       return E_RT_ILLNM;
155 
156    if (!(s = strtok_r(NULL, " \t", &b)))
157       return E_RT_SYNTAX;
158 
159    if (inet_pton(AF_INET6, s, &route6.gw) != 1)
160       return E_RT_SYNTAX;
161 
162    if (!has_tor_prefix(&route6.gw))
163       return E_RT_NOTORGW;
164 
165    if (IN6_ARE_ADDR_EQUAL(&route6.gw, &CNF(ocat_addr)))
166       return E_RT_GWSELF;
167 
168    ipv6_reduce(&route6.dest, route6.prefixlen);
169    if (ipv6_lookup_route(&route6.dest))
170       return E_RT_DUP;
171 
172    return ipv6_add_route(&route6);
173 }
174 
175