/* * Copyright 2012 Daniel Drown * * 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. * * getaddr.c - get a locally configured address */ #include <netinet/in.h> #include <strings.h> #include <string.h> #include <net/if.h> #include <linux/if_addr.h> #include <linux/rtnetlink.h> #include <netlink/handlers.h> #include <netlink/msg.h> #include "getaddr.h" #include "netlink_msg.h" #include "logging.h" // shared state between getinterface_ip and getaddr_cb struct target { int family; unsigned int ifindex; union anyip ip; int foundip; }; /* function: getaddr_cb * callback for getinterface_ip * msg - netlink message * data - (struct target) info for which address we're looking for */ static int getaddr_cb(struct nl_msg *msg, void *data) { struct ifaddrmsg *ifa_p; struct rtattr *rta_p; int rta_len; struct target *targ_p = (struct target *)data; ifa_p = (struct ifaddrmsg *)nlmsg_data(nlmsg_hdr(msg)); rta_p = (struct rtattr *)IFA_RTA(ifa_p); if(ifa_p->ifa_index != targ_p->ifindex) return NL_OK; if(ifa_p->ifa_scope != RT_SCOPE_UNIVERSE) return NL_OK; rta_len = RTM_PAYLOAD(nlmsg_hdr(msg)); for (; RTA_OK(rta_p, rta_len); rta_p = RTA_NEXT(rta_p, rta_len)) { switch(rta_p->rta_type) { case IFA_ADDRESS: if((targ_p->family == AF_INET6) && !(ifa_p->ifa_flags & IFA_F_SECONDARY)) { memcpy(&targ_p->ip.ip6, RTA_DATA(rta_p), rta_p->rta_len - sizeof(struct rtattr)); targ_p->foundip = 1; return NL_OK; } break; case IFA_LOCAL: if(targ_p->family == AF_INET) { memcpy(&targ_p->ip.ip4, RTA_DATA(rta_p), rta_p->rta_len - sizeof(struct rtattr)); targ_p->foundip = 1; return NL_OK; } break; } } return NL_OK; } /* function: error_handler * error callback for getinterface_ip * nla - source of the error message * err - netlink message * arg - (struct target) info for which address we're looking for */ static int error_handler(__attribute__((unused)) struct sockaddr_nl *nla, __attribute__((unused)) struct nlmsgerr *err, __attribute__((unused)) void *arg) { return NL_OK; } /* function: getinterface_ip * finds the first global non-privacy IP of the given family for the given interface, or returns NULL. caller frees pointer * interface - interface to look for * family - family */ union anyip *getinterface_ip(const char *interface, int family) { struct ifaddrmsg ifa; struct nl_cb *callbacks = NULL; struct target targ; union anyip *retval = NULL; targ.family = family; targ.foundip = 0; targ.ifindex = if_nametoindex(interface); if(targ.ifindex == 0) { return NULL; // interface not found } memset(&ifa, 0, sizeof(ifa)); ifa.ifa_family = targ.family; callbacks = nl_cb_alloc(NL_CB_DEFAULT); if(!callbacks) { goto cleanup; } nl_cb_set(callbacks, NL_CB_VALID, NL_CB_CUSTOM, getaddr_cb, &targ); nl_cb_err(callbacks, NL_CB_CUSTOM, error_handler, &targ); // sends message and waits for a response send_ifaddrmsg(RTM_GETADDR, NLM_F_REQUEST | NLM_F_ROOT, &ifa, callbacks); if(targ.foundip) { retval = malloc(sizeof(union anyip)); if(!retval) { logmsg(ANDROID_LOG_FATAL,"getinterface_ip/out of memory"); goto cleanup; } memcpy(retval, &targ.ip, sizeof(union anyip)); } cleanup: if(callbacks) nl_cb_put(callbacks); return retval; }