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