1 /* 2 * Copyright 2012 Daniel Drown 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 * getroute.c - get an ip route 17 */ 18 #include <string.h> 19 #include <errno.h> 20 21 #include <sys/socket.h> 22 #include <linux/netlink.h> 23 #include <linux/rtnetlink.h> 24 #include <arpa/inet.h> 25 26 #include <netlink/handlers.h> 27 #include <netlink/msg.h> 28 29 #include "getroute.h" 30 #include "netlink_callbacks.h" 31 #include "netlink_msg.h" 32 33 /* function: get_default_route_cb 34 * finds the default route with the request family and out interface and saves the gateway 35 * msg - netlink message 36 * data - (struct default_route_data) requested filters and response storage 37 */ 38 static int get_default_route_cb(struct nl_msg *msg, void *data) { 39 struct rtmsg *rt_p; 40 struct rtattr *rta_p; 41 int rta_len; 42 struct default_route_data *default_route = data; 43 union anyip *this_gateway = NULL; 44 ssize_t this_gateway_size; 45 int this_interface_id = -1; 46 47 if(default_route->reply_found_route) { // we already found our route 48 return NL_OK; 49 } 50 51 rt_p = (struct rtmsg *)nlmsg_data(nlmsg_hdr(msg)); 52 if(rt_p->rtm_dst_len != 0) { // not a default route 53 return NL_OK; 54 } 55 if((rt_p->rtm_family != default_route->request_family) || (rt_p->rtm_table != RT_TABLE_MAIN)) { // not a route we care about 56 return NL_OK; 57 } 58 59 rta_p = (struct rtattr *)RTM_RTA(rt_p); 60 rta_len = RTM_PAYLOAD(nlmsg_hdr(msg)); 61 for(; RTA_OK(rta_p, rta_len); rta_p = RTA_NEXT(rta_p, rta_len)) { 62 switch(rta_p->rta_type) { 63 case RTA_GATEWAY: 64 this_gateway = RTA_DATA(rta_p); 65 this_gateway_size = RTA_PAYLOAD(rta_p); 66 break; 67 case RTA_OIF: 68 this_interface_id = *(int *)RTA_DATA(rta_p); 69 break; 70 default: 71 break; 72 } 73 } 74 75 if(this_interface_id == default_route->request_interface_id) { 76 default_route->reply_found_route = 1; 77 if(this_gateway != NULL) { 78 memcpy(&default_route->reply_gateway, this_gateway, this_gateway_size); 79 default_route->reply_has_gateway = 1; 80 } else { 81 default_route->reply_has_gateway = 0; 82 } 83 } 84 return NL_OK; 85 } 86 87 /* function: error_handler 88 * error callback for get_default_route 89 * nla - where the message came from 90 * err - netlink message 91 * arg - (int *) storage for the error number 92 */ 93 static int error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err, void *arg) { 94 int *retval = arg; 95 if(err->error < 0) { // error_handler called even on no error (NLMSG_ERROR reply type used) 96 *retval = err->error; 97 } 98 return NL_OK; 99 } 100 101 /* function: get_default_route 102 * finds the first default route with the given family and interface, returns the gateway (if it exists) in the struct 103 * default_route - requested family and interface, and response storage 104 */ 105 int get_default_route(struct default_route_data *default_route) { 106 struct rtmsg msg; 107 struct nl_cb *callbacks = NULL; 108 struct nl_msg *nlmsg = NULL; 109 int retval = 0; 110 111 default_route->reply_has_gateway = 0; 112 default_route->reply_found_route = 0; 113 114 memset(&msg,'\0',sizeof(msg)); 115 msg.rtm_family = default_route->request_family; 116 msg.rtm_table = RT_TABLE_MAIN; 117 msg.rtm_protocol = RTPROT_KERNEL; 118 msg.rtm_scope = RT_SCOPE_UNIVERSE; 119 120 callbacks = nl_cb_alloc(NL_CB_DEFAULT); 121 if(!callbacks) { 122 retval = -ENOMEM; 123 goto cleanup; 124 } 125 // get_default_route_cb sets the response fields in default_route 126 nl_cb_set(callbacks, NL_CB_VALID, NL_CB_CUSTOM, get_default_route_cb, default_route); 127 nl_cb_err(callbacks, NL_CB_CUSTOM, error_handler, &retval); 128 129 nlmsg = nlmsg_alloc_rtmsg(RTM_GETROUTE, NLM_F_REQUEST | NLM_F_ROOT, &msg); 130 if(!nlmsg) { 131 retval = -ENOMEM; 132 goto cleanup; 133 } 134 send_netlink_msg(nlmsg, callbacks); 135 136 cleanup: 137 if(callbacks) 138 nl_cb_put(callbacks); 139 if(nlmsg) 140 nlmsg_free(nlmsg); 141 142 return retval; 143 } 144