diff options
Diffstat (limited to 'setroute.c')
-rw-r--r-- | setroute.c | 142 |
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; +} |