TODO RTNLGRP_LINK source3/lib/addrchange.c
[metze/samba/wip.git] / source3 / lib / addrchange.c
1 /*
2  * Samba Unix/Linux SMB client library
3  * Copyright (C) Volker Lendecke 2011
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18
19 #include "includes.h"
20 #include "lib/addrchange.h"
21 #include "../lib/util/tevent_ntstatus.h"
22
23 #ifdef HAVE_LINUX_RTNETLINK_H
24
25 #include "asm/types.h"
26 #include "linux/if.h"
27 #include "linux/netlink.h"
28 #include "linux/rtnetlink.h"
29 #include "lib/tsocket/tsocket.h"
30
31 struct addrchange_context {
32         struct tdgram_context *sock;
33 };
34
35 NTSTATUS addrchange_context_create(TALLOC_CTX *mem_ctx,
36                                    struct addrchange_context **pctx)
37 {
38         struct addrchange_context *ctx;
39         struct sockaddr_nl addr;
40         NTSTATUS status;
41         int sock = -1;
42         int res;
43         bool ok;
44
45         ctx = talloc(mem_ctx, struct addrchange_context);
46         if (ctx == NULL) {
47                 return NT_STATUS_NO_MEMORY;
48         }
49
50         sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
51         if (sock == -1) {
52                 status = map_nt_error_from_unix(errno);
53                 goto fail;
54         }
55
56         ok = smb_set_close_on_exec(sock);
57         if (!ok) {
58                 status = map_nt_error_from_unix(errno);
59                 goto fail;
60         }
61
62         res = set_blocking(sock, false);
63         if (res == -1) {
64                 status = map_nt_error_from_unix(errno);
65                 goto fail;
66         }
67
68         /*
69          * We're interested in address changes
70          */
71         ZERO_STRUCT(addr);
72         addr.nl_family = AF_NETLINK;
73         addr.nl_groups = RTNLGRP_LINK | RTMGRP_IPV6_IFADDR | RTMGRP_IPV4_IFADDR;
74
75         res = bind(sock, (struct sockaddr *)(void *)&addr, sizeof(addr));
76         if (res == -1) {
77                 status = map_nt_error_from_unix(errno);
78                 goto fail;
79         }
80
81         res = tdgram_bsd_existing_socket(ctx, sock, &ctx->sock);
82         if (res == -1) {
83                 status = map_nt_error_from_unix(errno);
84                 goto fail;
85         }
86
87         *pctx = ctx;
88         return NT_STATUS_OK;
89 fail:
90         if (sock != -1) {
91                 close(sock);
92         }
93         TALLOC_FREE(ctx);
94         return status;
95 }
96
97 struct addrchange_state {
98         struct tevent_context *ev;
99         struct addrchange_context *ctx;
100         uint8_t *buf;
101         struct tsocket_address *fromaddr;
102
103         enum addrchange_type type;
104         struct sockaddr_storage addr;
105 };
106
107 static void addrchange_done(struct tevent_req *subreq);
108
109 struct tevent_req *addrchange_send(TALLOC_CTX *mem_ctx,
110                                    struct tevent_context *ev,
111                                    struct addrchange_context *ctx)
112 {
113         struct tevent_req *req, *subreq;
114         struct addrchange_state *state;
115
116         req = tevent_req_create(mem_ctx, &state, struct addrchange_state);
117         if (req == NULL) {
118                 return NULL;
119         }
120         state->ev = ev;
121         state->ctx = ctx;
122
123         subreq = tdgram_recvfrom_send(state, state->ev, state->ctx->sock);
124         if (tevent_req_nomem(subreq, req)) {
125                 return tevent_req_post(req, state->ev);
126         }
127         tevent_req_set_callback(subreq, addrchange_done, req);
128         return req;
129 }
130
131 static void addrchange_done(struct tevent_req *subreq)
132 {
133         struct tevent_req *req = tevent_req_callback_data(
134                 subreq, struct tevent_req);
135         struct addrchange_state *state = tevent_req_data(
136                 req, struct addrchange_state);
137         union {
138                 struct sockaddr sa;
139                 struct sockaddr_nl nl;
140                 struct sockaddr_storage ss;
141         } fromaddr;
142         struct nlmsghdr *h;
143         struct ifaddrmsg *ifa;
144         struct ifinfomsg *ifi;
145         struct rtattr *rta;
146         ssize_t received;
147         int len;
148         int err;
149         bool found;
150
151         received = tdgram_recvfrom_recv(subreq, &err, state,
152                                         &state->buf,
153                                         &state->fromaddr);
154         TALLOC_FREE(subreq);
155         if (received == -1) {
156                 DEBUG(10, ("tdgram_recvfrom_recv returned %s\n", strerror(err)));
157                 tevent_req_nterror(req, map_nt_error_from_unix(err));
158                 return;
159         }
160         len = tsocket_address_bsd_sockaddr(state->fromaddr,
161                                            &fromaddr.sa,
162                                            sizeof(fromaddr));
163
164         if ((len != sizeof(fromaddr.nl) ||
165             fromaddr.sa.sa_family != AF_NETLINK))
166         {
167                 DEBUG(10, ("Got message from wrong addr\n"));
168                 goto retry;
169         }
170
171         if (fromaddr.nl.nl_pid != 0) {
172                 DEBUG(10, ("Got msg from pid %d, not from the kernel\n",
173                            (int)fromaddr.nl.nl_pid));
174                 goto retry;
175         }
176
177         if (received < sizeof(struct nlmsghdr)) {
178                 DEBUG(10, ("received %d, expected at least %d\n",
179                            (int)received, (int)sizeof(struct nlmsghdr)));
180                 goto retry;
181         }
182
183         h = (struct nlmsghdr *)state->buf;
184         if (h->nlmsg_len < sizeof(struct nlmsghdr)) {
185                 DEBUG(10, ("nlmsg_len=%d, expected at least %d\n",
186                            (int)h->nlmsg_len, (int)sizeof(struct nlmsghdr)));
187                 goto retry;
188         }
189         if (h->nlmsg_len > received) {
190                 DEBUG(10, ("nlmsg_len=%d, expected at most %d\n",
191                            (int)h->nlmsg_len, (int)received));
192                 goto retry;
193         }
194         switch (h->nlmsg_type) {
195         case RTM_NEWADDR:
196                 state->type = ADDRCHANGE_ADD;
197                 break;
198         case RTM_DELADDR:
199                 state->type = ADDRCHANGE_DEL;
200                 break;
201         case RTM_NEWLINK:
202                 state->type = ADDRCHANGE_LINK;
203                 goto process_link;
204                 break;
205         default:
206                 DEBUG(10, ("Got unexpected type %d - ignoring\n", h->nlmsg_type));
207                 goto retry;
208         }
209
210         if (h->nlmsg_len < sizeof(struct nlmsghdr)+sizeof(struct ifaddrmsg)) {
211                 DEBUG(10, ("nlmsg_len=%d, expected at least %d\n",
212                            (int)h->nlmsg_len,
213                            (int)(sizeof(struct nlmsghdr)
214                                  +sizeof(struct ifaddrmsg))));
215                 tevent_req_nterror(req, NT_STATUS_UNEXPECTED_IO_ERROR);
216                 return;
217         }
218
219         ifa = (struct ifaddrmsg *)NLMSG_DATA(h);
220
221         state->addr.ss_family = ifa->ifa_family;
222
223         len = h->nlmsg_len - sizeof(struct nlmsghdr) + sizeof(struct ifaddrmsg);
224
225         found = false;
226
227         for (rta = IFA_RTA(ifa); RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) {
228
229                 if ((rta->rta_type != IFA_LOCAL)
230                     && (rta->rta_type != IFA_ADDRESS)) {
231                         continue;
232                 }
233
234                 switch (ifa->ifa_family) {
235                 case AF_INET: {
236                         struct sockaddr_in *v4_addr;
237                         v4_addr = (struct sockaddr_in *)(void *)&state->addr;
238
239                         if (RTA_PAYLOAD(rta) != sizeof(uint32_t)) {
240                                 continue;
241                         }
242                         v4_addr->sin_addr.s_addr = *(uint32_t *)RTA_DATA(rta);
243                         found = true;
244                         break;
245                 }
246                 case AF_INET6: {
247                         struct sockaddr_in6 *v6_addr;
248                         v6_addr = (struct sockaddr_in6 *)(void *)&state->addr;
249
250                         if (RTA_PAYLOAD(rta) !=
251                             sizeof(v6_addr->sin6_addr.s6_addr)) {
252                                 continue;
253                         }
254                         memcpy(v6_addr->sin6_addr.s6_addr, RTA_DATA(rta),
255                                sizeof(v6_addr->sin6_addr.s6_addr));
256                         found = true;
257                         break;
258                 }
259                 }
260         }
261
262         if (!found) {
263                 tevent_req_nterror(req, NT_STATUS_INVALID_ADDRESS);
264                 return;
265         }
266
267         tevent_req_done(req);
268         return;
269
270 process_link:
271
272         if (h->nlmsg_len < sizeof(struct nlmsghdr)+sizeof(struct ifinfomsg)) {
273                 DEBUG(0, ("nlmsg_len=%d, expected at least %d\n",
274                            (int)h->nlmsg_len,
275                            (int)(sizeof(struct nlmsghdr)
276                                  +sizeof(struct ifinfomsg))));
277                 tevent_req_nterror(req, NT_STATUS_UNEXPECTED_IO_ERROR);
278                 return;
279         }
280
281         ifi = (struct ifinfomsg *)NLMSG_DATA(h);
282
283 DBG_ERR("ifi_type[%u] ifi_index[%d] ifi_flags[%u]\n",
284         ifi->ifi_type, ifi->ifi_index, ifi->ifi_flags);
285
286 DBG_ERR("IFF_UP: %u\n", ifi->ifi_flags & IFF_UP);
287 DBG_ERR("IFF_BROADCAST: %u\n", ifi->ifi_flags & IFF_BROADCAST);
288 DBG_ERR("IFF_DEBUG: %u\n", ifi->ifi_flags & IFF_DEBUG);
289 DBG_ERR("IFF_LOOPBACK: %u\n", ifi->ifi_flags & IFF_LOOPBACK);
290 DBG_ERR("IFF_POINTOPOINT: %u\n", ifi->ifi_flags & IFF_POINTOPOINT);
291 DBG_ERR("IFF_NOTRAILERS: %u\n", ifi->ifi_flags & IFF_NOTRAILERS);
292 DBG_ERR("IFF_RUNNING: %u\n", ifi->ifi_flags & IFF_RUNNING);
293 DBG_ERR("IFF_NOARP: %u\n", ifi->ifi_flags & IFF_NOARP);
294 DBG_ERR("IFF_PROMISC: %u\n", ifi->ifi_flags & IFF_PROMISC);
295 DBG_ERR("IFF_ALLMULTI: %u\n", ifi->ifi_flags & IFF_ALLMULTI);
296 DBG_ERR("IFF_MASTER: %u\n", ifi->ifi_flags & IFF_MASTER);
297 DBG_ERR("IFF_SLAVE: %u\n", ifi->ifi_flags & IFF_SLAVE);
298 DBG_ERR("IFF_MULTICAST: %u\n", ifi->ifi_flags & IFF_MULTICAST);
299 DBG_ERR("IFF_PORTSEL: %u\n", ifi->ifi_flags & IFF_PORTSEL);
300 DBG_ERR("IFF_AUTOMEDIA: %u\n", ifi->ifi_flags & IFF_AUTOMEDIA);
301 DBG_ERR("IFF_DYNAMIC: %u\n", ifi->ifi_flags & IFF_DYNAMIC);
302 DBG_ERR("IFF_LOWER_UP: %u\n", ifi->ifi_flags & IFF_LOWER_UP);
303 DBG_ERR("IFF_DORMANT: %u\n", ifi->ifi_flags & IFF_DORMANT);
304 DBG_ERR("IFF_ECHO: %u\n", ifi->ifi_flags & IFF_ECHO);
305
306         len = h->nlmsg_len - sizeof(struct nlmsghdr) + sizeof(struct ifinfomsg);
307
308         found = false;
309
310         for (rta = IFA_RTA(ifi); RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) {
311
312 DBG_ERR("rta_type[%u] RTA_PAYLOAD(%lu)\n", rta->rta_type, RTA_PAYLOAD(rta));
313
314                 switch (rta->rta_type) {
315                 case IFLA_LINK:
316                         if (RTA_PAYLOAD(rta) != sizeof(int)) {
317                                 continue;
318                         }
319
320                         found = true;
321                         break;
322                 case IFLA_MTU:
323                         if (RTA_PAYLOAD(rta) != sizeof(unsigned int)) {
324                                 continue;
325                         }
326
327                         found = true;
328                         break;
329                 default:
330                         continue;
331                 }
332
333         }
334
335         goto retry;
336
337         if (!found) {
338                 tevent_req_nterror(req, NT_STATUS_INVALID_ADDRESS);
339                 return;
340         }
341
342         tevent_req_done(req);
343         return;
344 retry:
345         TALLOC_FREE(state->buf);
346         TALLOC_FREE(state->fromaddr);
347
348         subreq = tdgram_recvfrom_send(state, state->ev, state->ctx->sock);
349         if (tevent_req_nomem(subreq, req)) {
350                 return;
351         }
352         tevent_req_set_callback(subreq, addrchange_done, req);
353 }
354
355 NTSTATUS addrchange_recv(struct tevent_req *req, enum addrchange_type *type,
356                          struct sockaddr_storage *addr)
357 {
358         struct addrchange_state *state = tevent_req_data(
359                 req, struct addrchange_state);
360         NTSTATUS status;
361
362         if (tevent_req_is_nterror(req, &status)) {
363                 tevent_req_received(req);
364                 return status;
365         }
366
367         *type = state->type;
368         *addr = state->addr;
369         tevent_req_received(req);
370         return NT_STATUS_OK;
371 }
372
373 #else
374
375 NTSTATUS addrchange_context_create(TALLOC_CTX *mem_ctx,
376                                    struct addrchange_context **pctx)
377 {
378         return NT_STATUS_NOT_SUPPORTED;
379 }
380
381 struct tevent_req *addrchange_send(TALLOC_CTX *mem_ctx,
382                                    struct tevent_context *ev,
383                                    struct addrchange_context *ctx)
384 {
385         return NULL;
386 }
387
388 NTSTATUS addrchange_recv(struct tevent_req *req, enum addrchange_type *type,
389                          struct sockaddr_storage *addr)
390 {
391         return NT_STATUS_NOT_IMPLEMENTED;
392 }
393
394 #endif