Daniel Drown | a45056e | 2012-03-23 10:42:54 -0500 | [diff] [blame^] | 1 | /* |
| 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 | */ |
| 44 | int 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 | |
| 137 | cleanup: |
| 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 | */ |
| 149 | void 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 | } |