chiark / gitweb /
return any locally configured IP address if they exist
[elogind.git] / netlink.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of nss-myhostname.
5
6   Copyright 2008-2011 Lennart Poettering
7
8   nss-myhostname is free software; you can redistribute it and/or
9   modify it under the terms of the GNU Lesser General Public License
10   as published by the Free Software Foundation; either version 2.1 of
11   the License, or (at your option) any later version.
12
13   nss-myhostname is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   Lesser General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public
19   License along with nss-myhostname; If not, see
20   <http://www.gnu.org/licenses/>.
21 ***/
22
23 #include <sys/socket.h>
24 #include <sys/un.h>
25 #include <asm/types.h>
26 #include <inttypes.h>
27 #include <linux/netlink.h>
28 #include <linux/rtnetlink.h>
29 #include <string.h>
30 #include <assert.h>
31 #include <errno.h>
32 #include <limits.h>
33 #include <arpa/inet.h>
34 #include <unistd.h>
35 #include <inttypes.h>
36 #include <stdlib.h>
37
38 #include "netlink.h"
39
40 static int address_compare(const void *_a, const void *_b) {
41         const struct address *a = _a, *b = _b;
42
43         /* Order lowest scope first, IPv4 before IPv6, lowest interface index first */
44
45         if (a->scope < b->scope)
46                 return -1;
47         if (a->scope > b->scope)
48                 return 1;
49
50         if (a->family == AF_INET && b->family == AF_INET6)
51                 return -1;
52         if (a->family == AF_INET6 && b->family == AF_INET)
53                 return 1;
54
55         if (a->ifindex < b->ifindex)
56                 return -1;
57         if (a->ifindex > b->ifindex)
58                 return 1;
59
60         return 0;
61 }
62
63 int netlink_acquire_addresses(struct address **_list, unsigned *_n_list) {
64
65         struct {
66                 struct nlmsghdr hdr;
67                 struct rtgenmsg gen;
68         } req;
69         struct rtgenmsg *gen;
70         int fd, r, on = 1;
71         uint32_t seq = 4711;
72         struct address *list = NULL;
73         unsigned n_list = 0;
74
75         fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
76         if (fd < 0)
77                 return -errno;
78
79         if (setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)) < 0) {
80                 r = -errno;
81                 goto finish;
82         }
83
84         memset(&req, 0, sizeof(req));
85         req.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg));
86         req.hdr.nlmsg_type = RTM_GETADDR;
87         req.hdr.nlmsg_flags = NLM_F_REQUEST|NLM_F_DUMP|NLM_F_ACK;
88         req.hdr.nlmsg_seq = seq;
89         req.hdr.nlmsg_pid = 0;
90
91         gen = NLMSG_DATA(&req.hdr);
92         gen->rtgen_family = AF_UNSPEC;
93
94         if (send(fd, &req, req.hdr.nlmsg_len, 0) < 0) {
95                 r = -errno;
96                 goto finish;
97         }
98
99         for (;;) {
100                 ssize_t bytes;
101                 struct msghdr msg;
102                 struct cmsghdr *cmsg;
103                 struct ucred *ucred;
104                 struct iovec iov;
105                 struct nlmsghdr *p;
106                 uint8_t cred_buffer[CMSG_SPACE(sizeof(struct ucred))];
107                 struct {
108                         struct nlmsghdr hdr;
109                         struct ifaddrmsg ifaddrmsg;
110                         uint8_t payload[16*1024];
111                 } resp;
112
113                 memset(&iov, 0, sizeof(iov));
114                 iov.iov_base = &resp;
115                 iov.iov_len = sizeof(resp);
116
117                 memset(&msg, 0, sizeof(msg));
118                 msg.msg_name = NULL;
119                 msg.msg_namelen = 0;
120                 msg.msg_iov = &iov;
121                 msg.msg_iovlen = 1;
122                 msg.msg_control = cred_buffer;
123                 msg.msg_controllen = sizeof(cred_buffer);
124                 msg.msg_flags = 0;
125
126                 bytes = recvmsg(fd, &msg, 0);
127                 if (bytes < 0) {
128                         r = -errno;
129                         goto finish;
130                 }
131
132                 cmsg = CMSG_FIRSTHDR(&msg);
133                 if (!cmsg || cmsg->cmsg_type != SCM_CREDENTIALS) {
134                         r = -EIO;
135                         goto finish;
136                 }
137
138                 ucred = (struct ucred*) CMSG_DATA(cmsg);
139                 if (ucred->uid != 0 || ucred->pid != 0)
140                         continue;
141
142                 for (p = &resp.hdr; bytes > 0; p = NLMSG_NEXT(p, bytes)) {
143                         struct ifaddrmsg *ifaddrmsg;
144                         struct rtattr *a;
145                         size_t l;
146                         void *local = NULL, *address = NULL;
147
148                         if (!NLMSG_OK(p, (size_t) bytes)) {
149                                 r = -EIO;
150                                 goto finish;
151                         }
152
153                         if (p->nlmsg_seq != seq)
154                                 continue;
155
156                         if (p->nlmsg_type == NLMSG_DONE) {
157                                 r = 0;
158                                 goto finish;
159                         }
160
161                         if (p->nlmsg_type == NLMSG_ERROR) {
162                                 struct nlmsgerr *nlmsgerr;
163
164                                 nlmsgerr = NLMSG_DATA(p);
165                                 r = -nlmsgerr->error;
166                                 goto finish;
167                         }
168
169                         if (p->nlmsg_type != RTM_NEWADDR)
170                                 continue;
171
172                         ifaddrmsg = NLMSG_DATA(p);
173
174                         if (ifaddrmsg->ifa_family != AF_INET &&
175                             ifaddrmsg->ifa_family != AF_INET6)
176                                 continue;
177
178                         if (ifaddrmsg->ifa_scope == RT_SCOPE_HOST ||
179                             ifaddrmsg->ifa_scope == RT_SCOPE_NOWHERE)
180                                 continue;
181
182                         if (ifaddrmsg->ifa_flags & IFA_F_DEPRECATED)
183                                 continue;
184
185                         l = NLMSG_PAYLOAD(p, sizeof(struct ifaddrmsg));
186                         a = IFA_RTA(ifaddrmsg);
187
188                         while (RTA_OK(a, l)) {
189
190                                 if (a->rta_type == IFA_ADDRESS)
191                                         address = RTA_DATA(a);
192                                 else if (a->rta_type == IFA_LOCAL)
193                                         local = RTA_DATA(a);
194
195                                 a = RTA_NEXT(a, l);
196                         }
197
198                         if (local)
199                                 address = local;
200
201                         if (!address)
202                                 continue;
203
204                         list = realloc(list, (n_list+1) * sizeof(struct address));
205                         if (!list) {
206                                 r = -ENOMEM;
207                                 goto finish;
208                         }
209
210                         list[n_list].family = ifaddrmsg->ifa_family;
211                         list[n_list].scope = ifaddrmsg->ifa_scope;
212                         memcpy(list[n_list].address, address, ifaddrmsg->ifa_family == AF_INET ? 4 : 16);
213                         list[n_list].ifindex = ifaddrmsg->ifa_index;
214
215                         n_list++;
216                 }
217         }
218
219 finish:
220         close(fd);
221
222         if (r < 0)
223                 free(list);
224         else {
225                 qsort(list, n_list, sizeof(struct address), address_compare);
226
227                 *_list = list;
228                 *_n_list = n_list;
229         }
230
231         return r;
232 }