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 #ifdef HAVE_LINUX_RTNETLINK_H
25 #include "asm/types.h"
26 #include "linux/netlink.h"
27 #include "linux/rtnetlink.h"
28 #include "lib/tsocket/tsocket.h"
30 struct addrchange_context
{
31 struct tdgram_context
*sock
;
34 NTSTATUS
addrchange_context_create(TALLOC_CTX
*mem_ctx
,
35 struct addrchange_context
**pctx
)
37 struct addrchange_context
*ctx
;
38 struct sockaddr_nl addr
;
44 ctx
= talloc(mem_ctx
, struct addrchange_context
);
46 return NT_STATUS_NO_MEMORY
;
49 sock
= socket(AF_NETLINK
, SOCK_RAW
, NETLINK_ROUTE
);
51 status
= map_nt_error_from_unix(errno
);
55 ok
= smb_set_close_on_exec(sock
);
57 status
= map_nt_error_from_unix(errno
);
61 res
= set_blocking(sock
, false);
63 status
= map_nt_error_from_unix(errno
);
68 * We're interested in address changes
71 addr
.nl_family
= AF_NETLINK
;
72 addr
.nl_groups
= RTMGRP_IPV6_IFADDR
| RTMGRP_IPV4_IFADDR
;
74 res
= bind(sock
, (struct sockaddr
*)(void *)&addr
, sizeof(addr
));
76 status
= map_nt_error_from_unix(errno
);
80 res
= tdgram_bsd_existing_socket(ctx
, sock
, &ctx
->sock
);
82 status
= map_nt_error_from_unix(errno
);
96 struct addrchange_state
{
97 struct tevent_context
*ev
;
98 struct addrchange_context
*ctx
;
100 struct tsocket_address
*fromaddr
;
102 enum addrchange_type type
;
103 struct sockaddr_storage addr
;
107 static void addrchange_done(struct tevent_req
*subreq
);
109 struct tevent_req
*addrchange_send(TALLOC_CTX
*mem_ctx
,
110 struct tevent_context
*ev
,
111 struct addrchange_context
*ctx
)
113 struct tevent_req
*req
, *subreq
;
114 struct addrchange_state
*state
;
116 req
= tevent_req_create(mem_ctx
, &state
, struct addrchange_state
);
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
);
127 tevent_req_set_callback(subreq
, addrchange_done
, req
);
131 static void addrchange_done(struct tevent_req
*subreq
)
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
);
139 struct sockaddr_nl nl
;
140 struct sockaddr_storage ss
;
143 struct ifaddrmsg
*ifa
;
150 received
= tdgram_recvfrom_recv(subreq
, &err
, state
,
154 if (received
== -1) {
155 DEBUG(10, ("tdgram_recvfrom_recv returned %s\n", strerror(err
)));
156 tevent_req_nterror(req
, map_nt_error_from_unix(err
));
159 len
= tsocket_address_bsd_sockaddr(state
->fromaddr
,
163 if ((len
!= sizeof(fromaddr
.nl
) ||
164 fromaddr
.sa
.sa_family
!= AF_NETLINK
))
166 DEBUG(10, ("Got message from wrong addr\n"));
170 if (fromaddr
.nl
.nl_pid
!= 0) {
171 DEBUG(10, ("Got msg from pid %d, not from the kernel\n",
172 (int)fromaddr
.nl
.nl_pid
));
176 if (received
< sizeof(struct nlmsghdr
)) {
177 DEBUG(10, ("received %d, expected at least %d\n",
178 (int)received
, (int)sizeof(struct nlmsghdr
)));
182 h
= (struct nlmsghdr
*)state
->buf
;
183 if (h
->nlmsg_len
< sizeof(struct nlmsghdr
)) {
184 DEBUG(10, ("nlmsg_len=%d, expected at least %d\n",
185 (int)h
->nlmsg_len
, (int)sizeof(struct nlmsghdr
)));
188 if (h
->nlmsg_len
> received
) {
189 DEBUG(10, ("nlmsg_len=%d, expected at most %d\n",
190 (int)h
->nlmsg_len
, (int)received
));
193 switch (h
->nlmsg_type
) {
195 state
->type
= ADDRCHANGE_ADD
;
198 state
->type
= ADDRCHANGE_DEL
;
201 DEBUG(10, ("Got unexpected type %d - ignoring\n", h
->nlmsg_type
));
205 if (h
->nlmsg_len
< sizeof(struct nlmsghdr
)+sizeof(struct ifaddrmsg
)) {
206 DEBUG(10, ("nlmsg_len=%d, expected at least %d\n",
208 (int)(sizeof(struct nlmsghdr
)
209 +sizeof(struct ifaddrmsg
))));
210 tevent_req_nterror(req
, NT_STATUS_UNEXPECTED_IO_ERROR
);
214 ifa
= (struct ifaddrmsg
*)NLMSG_DATA(h
);
216 state
->addr
.ss_family
= ifa
->ifa_family
;
217 state
->if_index
= ifa
->ifa_index
;
219 len
= h
->nlmsg_len
- sizeof(struct nlmsghdr
) + sizeof(struct ifaddrmsg
);
223 for (rta
= IFA_RTA(ifa
); RTA_OK(rta
, len
); rta
= RTA_NEXT(rta
, len
)) {
225 if ((rta
->rta_type
!= IFA_LOCAL
)
226 && (rta
->rta_type
!= IFA_ADDRESS
)) {
230 switch (ifa
->ifa_family
) {
232 struct sockaddr_in
*v4_addr
;
233 v4_addr
= (struct sockaddr_in
*)(void *)&state
->addr
;
235 if (RTA_PAYLOAD(rta
) != sizeof(uint32_t)) {
238 v4_addr
->sin_addr
.s_addr
= *(uint32_t *)RTA_DATA(rta
);
243 struct sockaddr_in6
*v6_addr
;
244 v6_addr
= (struct sockaddr_in6
*)(void *)&state
->addr
;
246 if (RTA_PAYLOAD(rta
) !=
247 sizeof(v6_addr
->sin6_addr
.s6_addr
)) {
250 memcpy(v6_addr
->sin6_addr
.s6_addr
, RTA_DATA(rta
),
251 sizeof(v6_addr
->sin6_addr
.s6_addr
));
259 tevent_req_nterror(req
, NT_STATUS_INVALID_ADDRESS
);
263 tevent_req_done(req
);
267 TALLOC_FREE(state
->buf
);
268 TALLOC_FREE(state
->fromaddr
);
270 subreq
= tdgram_recvfrom_send(state
, state
->ev
, state
->ctx
->sock
);
271 if (tevent_req_nomem(subreq
, req
)) {
274 tevent_req_set_callback(subreq
, addrchange_done
, req
);
277 NTSTATUS
addrchange_recv(struct tevent_req
*req
,
278 enum addrchange_type
*type
,
279 struct sockaddr_storage
*addr
,
282 struct addrchange_state
*state
= tevent_req_data(
283 req
, struct addrchange_state
);
286 if (tevent_req_is_nterror(req
, &status
)) {
287 tevent_req_received(req
);
293 if (if_index
!= NULL
) {
294 *if_index
= state
->if_index
;
296 tevent_req_received(req
);
302 NTSTATUS
addrchange_context_create(TALLOC_CTX
*mem_ctx
,
303 struct addrchange_context
**pctx
)
305 return NT_STATUS_NOT_SUPPORTED
;
308 struct tevent_req
*addrchange_send(TALLOC_CTX
*mem_ctx
,
309 struct tevent_context
*ev
,
310 struct addrchange_context
*ctx
)
315 NTSTATUS
addrchange_recv(struct tevent_req
*req
, enum addrchange_type
*type
,
316 struct sockaddr_storage
*addr
, uint32_t *if_index
)
318 return NT_STATUS_NOT_IMPLEMENTED
;