summaryrefslogtreecommitdiffstats
path: root/setroute.c
diff options
context:
space:
mode:
Diffstat (limited to 'setroute.c')
-rw-r--r--setroute.c186
1 files changed, 186 insertions, 0 deletions
diff --git a/setroute.c b/setroute.c
new file mode 100644
index 0000000..3edfe39
--- /dev/null
+++ b/setroute.c
@@ -0,0 +1,186 @@
+/*
+ * Copyright 2012 Daniel Drown <dan-android@drown.org>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * setroute.c - network route configuration
+ */
+#include <errno.h>
+#include <netinet/in.h>
+#include <net/if.h>
+
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <netlink/handlers.h>
+#include <netlink/msg.h>
+#include <netlink-types.h>
+
+#include "netlink_msg.h"
+#include "setroute.h"
+#include "logging.h"
+#include "getroute.h"
+
+/* function: if_route
+ * create/replace/delete a route
+ * ifname - name of the outbound interface
+ * family - AF_INET or AF_INET6
+ * destination - pointer to a struct in_addr or in6_addr for the destination network
+ * prefixlen - bitlength of the network address (example: 24 for AF_INET's 255.255.255.0)
+ * gateway - pointer to a struct in_addr or in6_addr for the gateway to use or NULL for an interface route
+ * metric - route metric (lower is better)
+ * mtu - route-specific mtu or 0 for the interface mtu
+ * change_type - ROUTE_DELETE, ROUTE_REPLACE, or ROUTE_CREATE
+ */
+int if_route(const char *ifname, int family, const void *destination, int prefixlen, const void *gateway, int metric, int mtu, int change_type) {
+ int retval;
+ struct nl_msg *msg = NULL;
+ struct rtmsg rt;
+ uint16_t type, flags = 0;
+ size_t addr_size;
+ uint32_t ifindex;
+
+ addr_size = inet_family_size(family);
+ if(addr_size == 0) {
+ retval = -EAFNOSUPPORT;
+ goto cleanup;
+ }
+
+ if (!(ifindex = if_nametoindex(ifname))) {
+ retval = -ENODEV;
+ goto cleanup;
+ }
+
+ memset(&rt, 0, sizeof(rt));
+ rt.rtm_family = family;
+ rt.rtm_table = RT_TABLE_MAIN;
+ rt.rtm_dst_len = prefixlen;
+ switch(change_type) {
+ case ROUTE_DELETE:
+ rt.rtm_scope = RT_SCOPE_NOWHERE;
+ type = RTM_DELROUTE;
+ break;
+
+ case ROUTE_REPLACE:
+ flags = NLM_F_REPLACE;
+ case ROUTE_CREATE:
+ type = RTM_NEWROUTE;
+ flags |= NLM_F_CREATE;
+ if(gateway == NULL) {
+ rt.rtm_scope = RT_SCOPE_LINK;
+ } else {
+ rt.rtm_scope = RT_SCOPE_UNIVERSE;
+ }
+ rt.rtm_type = RTN_UNICAST;
+ //RTPROT_STATIC = from administrator's configuration
+ //RTPROT_BOOT = from an automatic process
+ rt.rtm_protocol = RTPROT_BOOT;
+ break;
+
+ default:
+ retval = -EINVAL;
+ goto cleanup;
+ }
+
+ flags |= NLM_F_REQUEST | NLM_F_ACK;
+
+ msg = nlmsg_alloc_rtmsg(type, flags, &rt);
+ if(!msg) {
+ retval = -ENOMEM;
+ goto cleanup;
+ }
+
+ if(nla_put(msg, RTA_DST, addr_size, destination) < 0) {
+ retval = -ENOMEM;
+ goto cleanup;
+ }
+ if(gateway != NULL)
+ if(nla_put(msg, RTA_GATEWAY, addr_size, gateway) < 0) {
+ retval = -ENOMEM;
+ goto cleanup;
+ }
+ if(nla_put(msg, RTA_OIF, 4, &ifindex) < 0) {
+ retval = -ENOMEM;
+ goto cleanup;
+ }
+ if(nla_put(msg, RTA_PRIORITY, 4, &metric) < 0) {
+ retval = -ENOMEM;
+ goto cleanup;
+ }
+ if(mtu > 0 && change_type != ROUTE_DELETE) {
+ // MTU is inside an RTA_METRICS nested message
+ struct nlattr *metrics = nla_nest_start(msg, RTA_METRICS);
+ if(metrics == NULL) {
+ retval = -ENOMEM;
+ goto cleanup;
+ }
+
+ if(nla_put(msg, RTAX_MTU, 4, &mtu) < 0) {
+ retval = -ENOMEM;
+ goto cleanup;
+ }
+
+ nla_nest_end(msg, metrics);
+ }
+
+ retval = netlink_sendrecv(msg);
+
+cleanup:
+ if(msg)
+ nlmsg_free(msg);
+
+ return retval;
+}
+
+/* function: set_default_ipv6_route
+ * copies the default route on an interface (turns an RA route into a static
+ * route), which is needed to keep the route when forwarding is turned on
+ * device - interface to be the default route
+ */
+void set_default_ipv6_route(const char *device) {
+ struct in6_addr default_6 = IN6ADDR_ANY_INIT;
+ struct default_route_data default_route;
+ int status;
+ void *gateway = NULL;
+
+ memset(&default_route, '\0', sizeof(default_route));
+ default_route.request_family = AF_INET6;
+ default_route.request_interface_id = if_nametoindex(device);
+ if(default_route.request_interface_id == 0) {
+ logmsg(ANDROID_LOG_FATAL, "set_default_ipv6_route failed: no interface %s found", device);
+ exit(1);
+ }
+
+ status = get_default_route(&default_route);
+ if(status < 0) {
+ logmsg(ANDROID_LOG_FATAL, "set_default_ipv6_route/get_default_route failed: returned %d", status);
+ exit(1);
+ }
+
+ if(!default_route.reply_found_route) {
+ logmsg(ANDROID_LOG_FATAL, "set_default_ipv6_route/get_default_route failed: no default route found for %s", device);
+ exit(1);
+ }
+
+ if(default_route.reply_has_gateway) {
+ gateway = &default_route.reply_gateway.ip6;
+ }
+
+ if((status = if_route(device, AF_INET6, &default_6, 0, gateway, 1, 0, ROUTE_REPLACE)) < 0) {
+ if(status == -EEXIST) {
+ logmsg(ANDROID_LOG_WARN,"set_default_ipv6_route/if_route failed due to the route already existing");
+ } else {
+ logmsg(ANDROID_LOG_FATAL,"set_default_ipv6_route/if_route failed: %s",strerror(-status));
+ exit(1);
+ }
+ }
+}