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
;
106 static void addrchange_done(struct tevent_req
*subreq
);
108 struct tevent_req
*addrchange_send(TALLOC_CTX
*mem_ctx
,
109 struct tevent_context
*ev
,
110 struct addrchange_context
*ctx
)
112 struct tevent_req
*req
, *subreq
;
113 struct addrchange_state
*state
;
115 req
= tevent_req_create(mem_ctx
, &state
, struct addrchange_state
);
122 subreq
= tdgram_recvfrom_send(state
, state
->ev
, state
->ctx
->sock
);
123 if (tevent_req_nomem(subreq
, req
)) {
124 return tevent_req_post(req
, state
->ev
);
126 tevent_req_set_callback(subreq
, addrchange_done
, req
);
130 static void addrchange_done(struct tevent_req
*subreq
)
132 struct tevent_req
*req
= tevent_req_callback_data(
133 subreq
, struct tevent_req
);
134 struct addrchange_state
*state
= tevent_req_data(
135 req
, struct addrchange_state
);
138 struct sockaddr_nl nl
;
139 struct sockaddr_storage ss
;
142 struct ifaddrmsg
*ifa
;
149 received
= tdgram_recvfrom_recv(subreq
, &err
, state
,
153 if (received
== -1) {
154 DEBUG(10, ("tdgram_recvfrom_recv returned %s\n", strerror(err
)));
155 tevent_req_nterror(req
, map_nt_error_from_unix(err
));
158 len
= tsocket_address_bsd_sockaddr(state
->fromaddr
,
162 if ((len
!= sizeof(fromaddr
.nl
) ||
163 fromaddr
.sa
.sa_family
!= AF_NETLINK
))
165 DEBUG(10, ("Got message from wrong addr\n"));
169 if (fromaddr
.nl
.nl_pid
!= 0) {
170 DEBUG(10, ("Got msg from pid %d, not from the kernel\n",
171 (int)fromaddr
.nl
.nl_pid
));
175 if (received
< sizeof(struct nlmsghdr
)) {
176 DEBUG(10, ("received %d, expected at least %d\n",
177 (int)received
, (int)sizeof(struct nlmsghdr
)));
181 h
= (struct nlmsghdr
*)state
->buf
;
182 if (h
->nlmsg_len
< sizeof(struct nlmsghdr
)) {
183 DEBUG(10, ("nlmsg_len=%d, expected at least %d\n",
184 (int)h
->nlmsg_len
, (int)sizeof(struct nlmsghdr
)));
187 if (h
->nlmsg_len
> received
) {
188 DEBUG(10, ("nlmsg_len=%d, expected at most %d\n",
189 (int)h
->nlmsg_len
, (int)received
));
192 switch (h
->nlmsg_type
) {
194 state
->type
= ADDRCHANGE_ADD
;
197 state
->type
= ADDRCHANGE_DEL
;
200 DEBUG(10, ("Got unexpected type %d - ignoring\n", h
->nlmsg_type
));
204 if (h
->nlmsg_len
< sizeof(struct nlmsghdr
)+sizeof(struct ifaddrmsg
)) {
205 DEBUG(10, ("nlmsg_len=%d, expected at least %d\n",
207 (int)(sizeof(struct nlmsghdr
)
208 +sizeof(struct ifaddrmsg
))));
209 tevent_req_nterror(req
, NT_STATUS_UNEXPECTED_IO_ERROR
);
213 ifa
= (struct ifaddrmsg
*)NLMSG_DATA(h
);
215 state
->addr
.ss_family
= ifa
->ifa_family
;
217 len
= h
->nlmsg_len
- sizeof(struct nlmsghdr
) + sizeof(struct ifaddrmsg
);
221 for (rta
= IFA_RTA(ifa
); RTA_OK(rta
, len
); rta
= RTA_NEXT(rta
, len
)) {
223 if ((rta
->rta_type
!= IFA_LOCAL
)
224 && (rta
->rta_type
!= IFA_ADDRESS
)) {
228 switch (ifa
->ifa_family
) {
230 struct sockaddr_in
*v4_addr
;
231 v4_addr
= (struct sockaddr_in
*)(void *)&state
->addr
;
233 if (RTA_PAYLOAD(rta
) != sizeof(uint32_t)) {
236 v4_addr
->sin_addr
.s_addr
= *(uint32_t *)RTA_DATA(rta
);
241 struct sockaddr_in6
*v6_addr
;
242 v6_addr
= (struct sockaddr_in6
*)(void *)&state
->addr
;
244 if (RTA_PAYLOAD(rta
) !=
245 sizeof(v6_addr
->sin6_addr
.s6_addr
)) {
248 memcpy(v6_addr
->sin6_addr
.s6_addr
, RTA_DATA(rta
),
249 sizeof(v6_addr
->sin6_addr
.s6_addr
));
257 tevent_req_nterror(req
, NT_STATUS_INVALID_ADDRESS
);
261 tevent_req_done(req
);
265 TALLOC_FREE(state
->buf
);
266 TALLOC_FREE(state
->fromaddr
);
268 subreq
= tdgram_recvfrom_send(state
, state
->ev
, state
->ctx
->sock
);
269 if (tevent_req_nomem(subreq
, req
)) {
272 tevent_req_set_callback(subreq
, addrchange_done
, req
);
275 NTSTATUS
addrchange_recv(struct tevent_req
*req
, enum addrchange_type
*type
,
276 struct sockaddr_storage
*addr
)
278 struct addrchange_state
*state
= tevent_req_data(
279 req
, struct addrchange_state
);
282 if (tevent_req_is_nterror(req
, &status
)) {
283 tevent_req_received(req
);
289 tevent_req_received(req
);
295 NTSTATUS
addrchange_context_create(TALLOC_CTX
*mem_ctx
,
296 struct addrchange_context
**pctx
)
298 return NT_STATUS_NOT_SUPPORTED
;
301 struct tevent_req
*addrchange_send(TALLOC_CTX
*mem_ctx
,
302 struct tevent_context
*ev
,
303 struct addrchange_context
*ctx
)
308 NTSTATUS
addrchange_recv(struct tevent_req
*req
, enum addrchange_type
*type
,
309 struct sockaddr_storage
*addr
)
311 return NT_STATUS_NOT_IMPLEMENTED
;