/*
* 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;
}