summaryrefslogtreecommitdiffstats
path: root/setroute.c
diff options
context:
space:
mode:
Diffstat (limited to 'setroute.c')
-rw-r--r--setroute.c142
1 files changed, 142 insertions, 0 deletions
diff --git a/setroute.c b/setroute.c
new file mode 100644
index 0000000..f264387
--- /dev/null
+++ b/setroute.c
@@ -0,0 +1,142 @@
+/*
+ * 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;
+}