• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  * getaddr.c - get a locally configured address
17  */
18 #include "getaddr.h"
19 
20 #include <errno.h>
21 #include <linux/if_addr.h>
22 #include <linux/rtnetlink.h>
23 #include <net/if.h>
24 #include <netinet/in.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <strings.h>
28 #include <unistd.h>
29 
30 #include "logging.h"
31 
32 // Kernel suggests that keep the packet under 8KiB (NLMSG_GOODSIZE) in include/linux/netlink.h.
33 #define NLMSG_SIZE 8192
34 
35 // shared state between getinterface_ip and parse_ifaddrmsg
36 // TODO: refactor the communication between getinterface_ip and parse_ifaddrmsg because there
37 // is no netlink callback anymore.
38 struct target {
39   int family;
40   unsigned int ifindex;
41   union anyip ip;
42   int foundip;
43 };
44 
45 /* function: parse_ifaddrmsg
46  * parse ifaddrmsg for getinterface_ip
47  *   nh  - netlink message header
48  *   targ_p - (struct target) info for which address we're looking for
49  *            and the parsed result if any.
50  */
parse_ifaddrmsg(struct nlmsghdr * nh,struct target * targ_p)51 static void parse_ifaddrmsg(struct nlmsghdr *nh, struct target *targ_p) {
52   struct ifaddrmsg *ifa_p;
53   struct rtattr *rta_p;
54   int rta_len;
55 
56   ifa_p = (struct ifaddrmsg *)NLMSG_DATA(nh);
57   rta_p = (struct rtattr *)IFA_RTA(ifa_p);
58 
59   if (ifa_p->ifa_index != targ_p->ifindex) return;
60 
61   if (ifa_p->ifa_scope != RT_SCOPE_UNIVERSE) return;
62 
63   rta_len = IFA_PAYLOAD(nh);
64   for (; RTA_OK(rta_p, rta_len); rta_p = RTA_NEXT(rta_p, rta_len)) {
65     switch (rta_p->rta_type) {
66       case IFA_ADDRESS:
67         if ((targ_p->family == AF_INET6) && !(ifa_p->ifa_flags & IFA_F_SECONDARY)) {
68           memcpy(&targ_p->ip.ip6, RTA_DATA(rta_p), rta_p->rta_len - sizeof(struct rtattr));
69           targ_p->foundip = 1;
70           return;
71         }
72         break;
73       case IFA_LOCAL:
74         if (targ_p->family == AF_INET) {
75           memcpy(&targ_p->ip.ip4, RTA_DATA(rta_p), rta_p->rta_len - sizeof(struct rtattr));
76           targ_p->foundip = 1;
77           return;
78         }
79         break;
80     }
81   }
82 }
83 
sendrecv_ifaddrmsg(struct target * targ_p)84 void sendrecv_ifaddrmsg(struct target *targ_p) {
85   int s = socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, NETLINK_ROUTE);
86   if (s < 0) {
87     logmsg(ANDROID_LOG_ERROR, "open NETLINK_ROUTE socket failed %s", strerror(errno));
88     return;
89   }
90 
91   // Fill in netlink structures.
92   struct {
93     struct nlmsghdr n;
94     struct ifaddrmsg r;
95   } req = {
96     // Netlink message header.
97     .n.nlmsg_len   = NLMSG_LENGTH(sizeof(struct ifaddrmsg)),
98     .n.nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT,
99     .n.nlmsg_type  = RTM_GETADDR,
100 
101     // Interface address message header.
102     .r.ifa_family = targ_p->family,
103   };
104 
105   // Send interface address message.
106   if ((send(s, &req, req.n.nlmsg_len, 0)) < 0) {
107     logmsg(ANDROID_LOG_ERROR, "send netlink socket failed %s", strerror(errno));
108     close(s);
109     return;
110   }
111 
112   // Read interface address message and parse the result if any.
113   ssize_t bytes_read;
114   char buf[NLMSG_SIZE];
115   while ((bytes_read = recv(s, buf, sizeof(buf), 0)) > 0) {
116     struct nlmsghdr *nh = (struct nlmsghdr *)buf;
117     for (; NLMSG_OK(nh, bytes_read); nh = NLMSG_NEXT(nh, bytes_read)) {
118       if (nh->nlmsg_type == NLMSG_DONE) {
119         close(s);
120         return;
121       }
122       if (nh->nlmsg_type == NLMSG_ERROR) {
123         logmsg(ANDROID_LOG_ERROR, "netlink message error");
124         close(s);
125         return;
126       }
127       if (nh->nlmsg_type == RTM_NEWADDR) {
128         // Walk through the all messages and update struct target variable as the deleted
129         // callback behavior of getaddr_cb() which always returns NL_OK.
130         // TODO: review if this can early return once address has been found.
131         parse_ifaddrmsg(nh, targ_p);
132       }
133     }
134   }
135   close(s);
136 }
137 
138 /* function: getinterface_ip
139  * finds the first global non-privacy IP of the given family for the given interface, or returns
140  * NULL.  caller frees pointer
141  *   interface - interface to look for
142  *   family    - family
143  */
getinterface_ip(const char * interface,int family)144 union anyip *getinterface_ip(const char *interface, int family) {
145   union anyip *retval = NULL;
146   struct target targ  = {
147     .family  = family,
148     .foundip = 0,
149     .ifindex = if_nametoindex(interface),
150   };
151 
152   if (targ.ifindex == 0) {
153     return NULL;  // interface not found
154   }
155 
156   // sends message and receives the response.
157   sendrecv_ifaddrmsg(&targ);
158 
159   if (targ.foundip) {
160     retval = malloc(sizeof(union anyip));
161     if (!retval) {
162       logmsg(ANDROID_LOG_FATAL, "getinterface_ip/out of memory");
163       return NULL;
164     }
165     memcpy(retval, &targ.ip, sizeof(union anyip));
166   }
167 
168   return retval;
169 }
170