chiark / gitweb /
Use legacy getifaddrs() on GNU/kFreeBSD
[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 "ifconf.h"
39
40 int ifconf_acquire_addresses(struct address **_list, unsigned *_n_list) {
41
42         struct {
43                 struct nlmsghdr hdr;
44                 struct rtgenmsg gen;
45         } req;
46         struct rtgenmsg *gen;
47         int fd, r, on = 1;
48         uint32_t seq = 4711;
49         struct address *list = NULL;
50         unsigned n_list = 0;
51
52         fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
53         if (fd < 0)
54                 return -errno;
55
56         if (setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)) < 0) {
57                 r = -errno;
58                 goto finish;
59         }
60
61         memset(&req, 0, sizeof(req));
62         req.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg));
63         req.hdr.nlmsg_type = RTM_GETADDR;
64         req.hdr.nlmsg_flags = NLM_F_REQUEST|NLM_F_DUMP|NLM_F_ACK;
65         req.hdr.nlmsg_seq = seq;
66         req.hdr.nlmsg_pid = 0;
67
68         gen = NLMSG_DATA(&req.hdr);
69         gen->rtgen_family = AF_UNSPEC;
70
71         if (send(fd, &req, req.hdr.nlmsg_len, 0) < 0) {
72                 r = -errno;
73                 goto finish;
74         }
75
76         for (;;) {
77                 ssize_t bytes;
78                 struct msghdr msg;
79                 struct cmsghdr *cmsg;
80                 struct ucred *ucred;
81                 struct iovec iov;
82                 struct nlmsghdr *p;
83                 uint8_t cred_buffer[CMSG_SPACE(sizeof(struct ucred))];
84                 struct {
85                         struct nlmsghdr hdr;
86                         struct ifaddrmsg ifaddrmsg;
87                         uint8_t payload[16*1024];
88                 } resp;
89
90                 memset(&iov, 0, sizeof(iov));
91                 iov.iov_base = &resp;
92                 iov.iov_len = sizeof(resp);
93
94                 memset(&msg, 0, sizeof(msg));
95                 msg.msg_name = NULL;
96                 msg.msg_namelen = 0;
97                 msg.msg_iov = &iov;
98                 msg.msg_iovlen = 1;
99                 msg.msg_control = cred_buffer;
100                 msg.msg_controllen = sizeof(cred_buffer);
101                 msg.msg_flags = 0;
102
103                 bytes = recvmsg(fd, &msg, 0);
104                 if (bytes < 0) {
105                         r = -errno;
106                         goto finish;
107                 }
108
109                 cmsg = CMSG_FIRSTHDR(&msg);
110                 if (!cmsg || cmsg->cmsg_type != SCM_CREDENTIALS) {
111                         r = -EIO;
112                         goto finish;
113                 }
114
115                 ucred = (struct ucred*) CMSG_DATA(cmsg);
116                 if (ucred->uid != 0 || ucred->pid != 0)
117                         continue;
118
119                 for (p = &resp.hdr; bytes > 0; p = NLMSG_NEXT(p, bytes)) {
120                         struct ifaddrmsg *ifaddrmsg;
121                         struct rtattr *a;
122                         size_t l;
123                         void *local = NULL, *address = NULL;
124
125                         if (!NLMSG_OK(p, (size_t) bytes)) {
126                                 r = -EIO;
127                                 goto finish;
128                         }
129
130                         if (p->nlmsg_seq != seq)
131                                 continue;
132
133                         if (p->nlmsg_type == NLMSG_DONE) {
134                                 r = 0;
135                                 goto finish;
136                         }
137
138                         if (p->nlmsg_type == NLMSG_ERROR) {
139                                 struct nlmsgerr *nlmsgerr;
140
141                                 nlmsgerr = NLMSG_DATA(p);
142                                 r = -nlmsgerr->error;
143                                 goto finish;
144                         }
145
146                         if (p->nlmsg_type != RTM_NEWADDR)
147                                 continue;
148
149                         ifaddrmsg = NLMSG_DATA(p);
150
151                         if (ifaddrmsg->ifa_family != AF_INET &&
152                             ifaddrmsg->ifa_family != AF_INET6)
153                                 continue;
154
155                         if (ifaddrmsg->ifa_scope == RT_SCOPE_HOST ||
156                             ifaddrmsg->ifa_scope == RT_SCOPE_NOWHERE)
157                                 continue;
158
159                         if (ifaddrmsg->ifa_flags & IFA_F_DEPRECATED)
160                                 continue;
161
162                         l = NLMSG_PAYLOAD(p, sizeof(struct ifaddrmsg));
163                         a = IFA_RTA(ifaddrmsg);
164
165                         while (RTA_OK(a, l)) {
166
167                                 if (a->rta_type == IFA_ADDRESS)
168                                         address = RTA_DATA(a);
169                                 else if (a->rta_type == IFA_LOCAL)
170                                         local = RTA_DATA(a);
171
172                                 a = RTA_NEXT(a, l);
173                         }
174
175                         if (local)
176                                 address = local;
177
178                         if (!address)
179                                 continue;
180
181                         list = realloc(list, (n_list+1) * sizeof(struct address));
182                         if (!list) {
183                                 r = -ENOMEM;
184                                 goto finish;
185                         }
186
187                         list[n_list].family = ifaddrmsg->ifa_family;
188                         list[n_list].scope = ifaddrmsg->ifa_scope;
189                         memcpy(list[n_list].address, address, ifaddrmsg->ifa_family == AF_INET ? 4 : 16);
190                         list[n_list].ifindex = ifaddrmsg->ifa_index;
191
192                         n_list++;
193                 }
194         }
195
196 finish:
197         close(fd);
198
199         if (r < 0)
200                 free(list);
201         else {
202                 qsort(list, n_list, sizeof(struct address), address_compare);
203
204                 *_list = list;
205                 *_n_list = n_list;
206         }
207
208         return r;
209 }