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 */
if_route(const char * ifname,int family,const void * destination,int prefixlen,const void * gateway,int metric,int mtu,int change_type)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