blob: 3edfe39d593c69e193fe607d3e9cf0b11aa9de1f [file] [log] [blame]
Daniel Drowna45056e2012-03-23 10:42:54 -05001/*
2 * Copyright 2012 Daniel Drown <dan-android@drown.org>
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 * setroute.c - network route configuration
17 */
18#include <errno.h>
19#include <netinet/in.h>
20#include <net/if.h>
21
22#include <linux/netlink.h>
23#include <linux/rtnetlink.h>
24#include <netlink/handlers.h>
25#include <netlink/msg.h>
26#include <netlink-types.h>
27
28#include "netlink_msg.h"
29#include "setroute.h"
30#include "logging.h"
31#include "getroute.h"
32
33/* function: if_route
34 * create/replace/delete a route
35 * ifname - name of the outbound interface
36 * family - AF_INET or AF_INET6
37 * destination - pointer to a struct in_addr or in6_addr for the destination network
38 * prefixlen - bitlength of the network address (example: 24 for AF_INET's 255.255.255.0)
39 * gateway - pointer to a struct in_addr or in6_addr for the gateway to use or NULL for an interface route
40 * metric - route metric (lower is better)
41 * mtu - route-specific mtu or 0 for the interface mtu
42 * change_type - ROUTE_DELETE, ROUTE_REPLACE, or ROUTE_CREATE
43 */
44int if_route(const char *ifname, int family, const void *destination, int prefixlen, const void *gateway, int metric, int mtu, int change_type) {
45 int retval;
46 struct nl_msg *msg = NULL;
47 struct rtmsg rt;
48 uint16_t type, flags = 0;
49 size_t addr_size;
50 uint32_t ifindex;
51
52 addr_size = inet_family_size(family);
53 if(addr_size == 0) {
54 retval = -EAFNOSUPPORT;
55 goto cleanup;
56 }
57
58 if (!(ifindex = if_nametoindex(ifname))) {
59 retval = -ENODEV;
60 goto cleanup;
61 }
62
63 memset(&rt, 0, sizeof(rt));
64 rt.rtm_family = family;
65 rt.rtm_table = RT_TABLE_MAIN;
66 rt.rtm_dst_len = prefixlen;
67 switch(change_type) {
68 case ROUTE_DELETE:
69 rt.rtm_scope = RT_SCOPE_NOWHERE;
70 type = RTM_DELROUTE;
71 break;
72
73 case ROUTE_REPLACE:
74 flags = NLM_F_REPLACE;
75 case ROUTE_CREATE:
76 type = RTM_NEWROUTE;
77 flags |= NLM_F_CREATE;
78 if(gateway == NULL) {
79 rt.rtm_scope = RT_SCOPE_LINK;
80 } else {
81 rt.rtm_scope = RT_SCOPE_UNIVERSE;
82 }
83 rt.rtm_type = RTN_UNICAST;
84 //RTPROT_STATIC = from administrator's configuration
85 //RTPROT_BOOT = from an automatic process
86 rt.rtm_protocol = RTPROT_BOOT;
87 break;
88
89 default:
90 retval = -EINVAL;
91 goto cleanup;
92 }
93
94 flags |= NLM_F_REQUEST | NLM_F_ACK;
95
96 msg = nlmsg_alloc_rtmsg(type, flags, &rt);
97 if(!msg) {
98 retval = -ENOMEM;
99 goto cleanup;
100 }
101
102 if(nla_put(msg, RTA_DST, addr_size, destination) < 0) {
103 retval = -ENOMEM;
104 goto cleanup;
105 }
106 if(gateway != NULL)
107 if(nla_put(msg, RTA_GATEWAY, addr_size, gateway) < 0) {
108 retval = -ENOMEM;
109 goto cleanup;
110 }
111 if(nla_put(msg, RTA_OIF, 4, &ifindex) < 0) {
112 retval = -ENOMEM;
113 goto cleanup;
114 }
115 if(nla_put(msg, RTA_PRIORITY, 4, &metric) < 0) {
116 retval = -ENOMEM;
117 goto cleanup;
118 }
119 if(mtu > 0 && change_type != ROUTE_DELETE) {
120 // MTU is inside an RTA_METRICS nested message
121 struct nlattr *metrics = nla_nest_start(msg, RTA_METRICS);
122 if(metrics == NULL) {
123 retval = -ENOMEM;
124 goto cleanup;
125 }
126
127 if(nla_put(msg, RTAX_MTU, 4, &mtu) < 0) {
128 retval = -ENOMEM;
129 goto cleanup;
130 }
131
132 nla_nest_end(msg, metrics);
133 }
134
135 retval = netlink_sendrecv(msg);
136
137cleanup:
138 if(msg)
139 nlmsg_free(msg);
140
141 return retval;
142}
143
144/* function: set_default_ipv6_route
145 * copies the default route on an interface (turns an RA route into a static
146 * route), which is needed to keep the route when forwarding is turned on
147 * device - interface to be the default route
148 */
149void set_default_ipv6_route(const char *device) {
150 struct in6_addr default_6 = IN6ADDR_ANY_INIT;
151 struct default_route_data default_route;
152 int status;
153 void *gateway = NULL;
154
155 memset(&default_route, '\0', sizeof(default_route));
156 default_route.request_family = AF_INET6;
157 default_route.request_interface_id = if_nametoindex(device);
158 if(default_route.request_interface_id == 0) {
159 logmsg(ANDROID_LOG_FATAL, "set_default_ipv6_route failed: no interface %s found", device);
160 exit(1);
161 }
162
163 status = get_default_route(&default_route);
164 if(status < 0) {
165 logmsg(ANDROID_LOG_FATAL, "set_default_ipv6_route/get_default_route failed: returned %d", status);
166 exit(1);
167 }
168
169 if(!default_route.reply_found_route) {
170 logmsg(ANDROID_LOG_FATAL, "set_default_ipv6_route/get_default_route failed: no default route found for %s", device);
171 exit(1);
172 }
173
174 if(default_route.reply_has_gateway) {
175 gateway = &default_route.reply_gateway.ip6;
176 }
177
178 if((status = if_route(device, AF_INET6, &default_6, 0, gateway, 1, 0, ROUTE_REPLACE)) < 0) {
179 if(status == -EEXIST) {
180 logmsg(ANDROID_LOG_WARN,"set_default_ipv6_route/if_route failed due to the route already existing");
181 } else {
182 logmsg(ANDROID_LOG_FATAL,"set_default_ipv6_route/if_route failed: %s",strerror(-status));
183 exit(1);
184 }
185 }
186}