2
* Samba Unix/Linux SMB client library
3
* Copyright (C) Volker Lendecke 2011
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.
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.
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/>.
20
#include "lib/addrchange.h"
21
#include "../lib/util/tevent_ntstatus.h"
23
#if HAVE_LINUX_RTNETLINK_H
25
#include "asm/types.h"
26
#include "linux/netlink.h"
27
#include "linux/rtnetlink.h"
28
#include "lib/async_req/async_sock.h"
30
struct addrchange_context {
34
static int addrchange_context_destructor(struct addrchange_context *c);
36
NTSTATUS addrchange_context_create(TALLOC_CTX *mem_ctx,
37
struct addrchange_context **pctx)
39
struct addrchange_context *ctx;
40
struct sockaddr_nl addr;
44
ctx = talloc(mem_ctx, struct addrchange_context);
46
return NT_STATUS_NO_MEMORY;
49
ctx->sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
50
if (ctx->sock == -1) {
51
status = map_nt_error_from_unix(errno);
54
talloc_set_destructor(ctx, addrchange_context_destructor);
57
* We're interested in address changes
60
addr.nl_family = AF_NETLINK;
61
addr.nl_groups = RTMGRP_IPV6_IFADDR | RTMGRP_IPV4_IFADDR;
63
res = bind(ctx->sock, (struct sockaddr *)(void *)&addr, sizeof(addr));
65
status = map_nt_error_from_unix(errno);
76
static int addrchange_context_destructor(struct addrchange_context *c)
85
struct addrchange_state {
86
struct tevent_context *ev;
87
struct addrchange_context *ctx;
89
struct sockaddr_storage fromaddr;
90
socklen_t fromaddr_len;
92
enum addrchange_type type;
93
struct sockaddr_storage addr;
96
static void addrchange_done(struct tevent_req *subreq);
98
struct tevent_req *addrchange_send(TALLOC_CTX *mem_ctx,
99
struct tevent_context *ev,
100
struct addrchange_context *ctx)
102
struct tevent_req *req, *subreq;
103
struct addrchange_state *state;
105
req = tevent_req_create(mem_ctx, &state, struct addrchange_state);
112
state->fromaddr_len = sizeof(state->fromaddr);
113
subreq = recvfrom_send(state, state->ev, state->ctx->sock,
114
state->buf, sizeof(state->buf), 0,
115
&state->fromaddr, &state->fromaddr_len);
116
if (tevent_req_nomem(subreq, req)) {
117
return tevent_req_post(req, state->ev);
119
tevent_req_set_callback(subreq, addrchange_done, req);
123
static void addrchange_done(struct tevent_req *subreq)
125
struct tevent_req *req = tevent_req_callback_data(
126
subreq, struct tevent_req);
127
struct addrchange_state *state = tevent_req_data(
128
req, struct addrchange_state);
129
struct sockaddr_nl *addr;
131
struct ifaddrmsg *ifa;
138
received = recvfrom_recv(subreq, &err);
140
if (received == -1) {
141
DEBUG(10, ("recvfrom returned %s\n", strerror(errno)));
142
tevent_req_nterror(req, map_nt_error_from_unix(err));
145
if ((state->fromaddr_len != sizeof(struct sockaddr_nl))
146
|| (state->fromaddr.ss_family != AF_NETLINK)) {
147
DEBUG(10, ("Got message from wrong addr\n"));
151
addr = (struct sockaddr_nl *)(void *)&state->addr;
152
if (addr->nl_pid != 0) {
153
DEBUG(10, ("Got msg from pid %d, not from the kernel\n",
158
if (received < sizeof(struct nlmsghdr)) {
159
DEBUG(10, ("received %d, expected at least %d\n",
160
(int)received, (int)sizeof(struct nlmsghdr)));
164
h = (struct nlmsghdr *)state->buf;
165
if (h->nlmsg_len < sizeof(struct nlmsghdr)) {
166
DEBUG(10, ("nlmsg_len=%d, expected at least %d\n",
167
(int)h->nlmsg_len, (int)sizeof(struct nlmsghdr)));
170
if (h->nlmsg_len > received) {
171
DEBUG(10, ("nlmsg_len=%d, expected at most %d\n",
172
(int)h->nlmsg_len, (int)received));
175
switch (h->nlmsg_type) {
177
state->type = ADDRCHANGE_ADD;
180
state->type = ADDRCHANGE_DEL;
183
DEBUG(10, ("Got unexpected type %d - ignoring\n", h->nlmsg_type));
187
if (h->nlmsg_len < sizeof(struct nlmsghdr)+sizeof(struct ifaddrmsg)) {
188
DEBUG(10, ("nlmsg_len=%d, expected at least %d\n",
190
(int)(sizeof(struct nlmsghdr)
191
+sizeof(struct ifaddrmsg))));
192
tevent_req_nterror(req, NT_STATUS_UNEXPECTED_IO_ERROR);
196
ifa = (struct ifaddrmsg *)NLMSG_DATA(h);
198
state->addr.ss_family = ifa->ifa_family;
201
len = h->nlmsg_len - sizeof(struct nlmsghdr) + sizeof(struct ifaddrmsg);
205
for (rta = IFA_RTA(ifa); RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) {
207
if ((rta->rta_type != IFA_LOCAL)
208
&& (rta->rta_type != IFA_ADDRESS)) {
212
switch (ifa->ifa_family) {
214
struct sockaddr_in *v4_addr;
215
v4_addr = (struct sockaddr_in *)(void *)&state->addr;
217
if (RTA_PAYLOAD(rta) != sizeof(uint32_t)) {
220
v4_addr->sin_addr.s_addr = *(uint32_t *)RTA_DATA(rta);
225
struct sockaddr_in6 *v6_addr;
226
v6_addr = (struct sockaddr_in6 *)(void *)&state->addr;
228
if (RTA_PAYLOAD(rta) !=
229
sizeof(v6_addr->sin6_addr.s6_addr)) {
232
memcpy(v6_addr->sin6_addr.s6_addr, RTA_DATA(rta),
233
sizeof(v6_addr->sin6_addr.s6_addr));
241
tevent_req_nterror(req, NT_STATUS_INVALID_ADDRESS);
245
tevent_req_done(req);
249
state->fromaddr_len = sizeof(state->fromaddr);
250
subreq = recvfrom_send(state, state->ev, state->ctx->sock,
251
state->buf, sizeof(state->buf), 0,
252
&state->fromaddr, &state->fromaddr_len);
253
if (tevent_req_nomem(subreq, req)) {
256
tevent_req_set_callback(subreq, addrchange_done, req);
259
NTSTATUS addrchange_recv(struct tevent_req *req, enum addrchange_type *type,
260
struct sockaddr_storage *addr)
262
struct addrchange_state *state = tevent_req_data(
263
req, struct addrchange_state);
266
if (tevent_req_is_nterror(req, &status)) {
277
NTSTATUS addrchange_context_create(TALLOC_CTX *mem_ctx,
278
struct addrchange_context **pctx)
280
return NT_STATUS_NOT_SUPPORTED;
283
struct tevent_req *addrchange_send(TALLOC_CTX *mem_ctx,
284
struct tevent_context *ev,
285
struct addrchange_context *ctx)
290
NTSTATUS addrchange_recv(struct tevent_req *req, enum addrchange_type *type,
291
struct sockaddr_storage *addr)
293
return NT_STATUS_NOT_IMPLEMENTED;