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"
22 #if HAVE_LINUX_RTNETLINK_H
24 #include "asm/types.h"
25 #include "linux/netlink.h"
26 #include "linux/rtnetlink.h"
27 #include "lib/async_req/async_sock.h"
29 struct addrchange_context
{
33 static int addrchange_context_destructor(struct addrchange_context
*c
);
35 NTSTATUS
addrchange_context_create(TALLOC_CTX
*mem_ctx
,
36 struct addrchange_context
**pctx
)
38 struct addrchange_context
*ctx
;
39 struct sockaddr_nl addr
;
43 ctx
= talloc(mem_ctx
, struct addrchange_context
);
45 return NT_STATUS_NO_MEMORY
;
48 ctx
->sock
= socket(AF_NETLINK
, SOCK_RAW
, NETLINK_ROUTE
);
49 if (ctx
->sock
== -1) {
50 status
= map_nt_error_from_unix(errno
);
53 talloc_set_destructor(ctx
, addrchange_context_destructor
);
56 * We're interested in address changes
59 addr
.nl_family
= AF_NETLINK
;
60 addr
.nl_groups
= RTMGRP_IPV6_IFADDR
| RTMGRP_IPV4_IFADDR
;
62 res
= bind(ctx
->sock
, (struct sockaddr
*)(void *)&addr
, sizeof(addr
));
64 status
= map_nt_error_from_unix(errno
);
75 static int addrchange_context_destructor(struct addrchange_context
*c
)
84 struct addrchange_state
{
85 struct tevent_context
*ev
;
86 struct addrchange_context
*ctx
;
88 struct sockaddr_storage fromaddr
;
89 socklen_t fromaddr_len
;
91 enum addrchange_type type
;
92 struct sockaddr_storage addr
;
95 static void addrchange_done(struct tevent_req
*subreq
);
97 struct tevent_req
*addrchange_send(TALLOC_CTX
*mem_ctx
,
98 struct tevent_context
*ev
,
99 struct addrchange_context
*ctx
)
101 struct tevent_req
*req
, *subreq
;
102 struct addrchange_state
*state
;
104 req
= tevent_req_create(mem_ctx
, &state
, struct addrchange_state
);
111 state
->fromaddr_len
= sizeof(state
->fromaddr
);
112 subreq
= recvfrom_send(state
, state
->ev
, state
->ctx
->sock
,
113 state
->buf
, sizeof(state
->buf
), 0,
114 &state
->fromaddr
, &state
->fromaddr_len
);
115 if (tevent_req_nomem(subreq
, req
)) {
116 return tevent_req_post(req
, state
->ev
);
118 tevent_req_set_callback(subreq
, addrchange_done
, req
);
122 static void addrchange_done(struct tevent_req
*subreq
)
124 struct tevent_req
*req
= tevent_req_callback_data(
125 subreq
, struct tevent_req
);
126 struct addrchange_state
*state
= tevent_req_data(
127 req
, struct addrchange_state
);
128 struct sockaddr_nl
*addr
;
130 struct ifaddrmsg
*ifa
;
137 received
= recvfrom_recv(subreq
, &err
);
139 if (received
== -1) {
140 DEBUG(10, ("recvfrom returned %s\n", strerror(errno
)));
141 tevent_req_nterror(req
, map_nt_error_from_unix(err
));
144 if ((state
->fromaddr_len
!= sizeof(struct sockaddr_nl
))
145 || (state
->fromaddr
.ss_family
!= AF_NETLINK
)) {
146 DEBUG(10, ("Got message from wrong addr\n"));
150 addr
= (struct sockaddr_nl
*)(void *)&state
->addr
;
151 if (addr
->nl_pid
!= 0) {
152 DEBUG(10, ("Got msg from pid %d, not from the kernel\n",
157 if (received
< sizeof(struct nlmsghdr
)) {
158 DEBUG(10, ("received %d, expected at least %d\n",
159 (int)received
, (int)sizeof(struct nlmsghdr
)));
163 h
= (struct nlmsghdr
*)state
->buf
;
164 if (h
->nlmsg_len
< sizeof(struct nlmsghdr
)) {
165 DEBUG(10, ("nlmsg_len=%d, expected at least %d\n",
166 (int)h
->nlmsg_len
, (int)sizeof(struct nlmsghdr
)));
169 if (h
->nlmsg_len
> received
) {
170 DEBUG(10, ("nlmsg_len=%d, expected at most %d\n",
171 (int)h
->nlmsg_len
, (int)received
));
174 switch (h
->nlmsg_type
) {
176 state
->type
= ADDRCHANGE_ADD
;
179 state
->type
= ADDRCHANGE_DEL
;
182 DEBUG(10, ("Got unexpected type %d - ignoring\n", h
->nlmsg_type
));
186 if (h
->nlmsg_len
< sizeof(struct nlmsghdr
)+sizeof(struct ifaddrmsg
)) {
187 DEBUG(10, ("nlmsg_len=%d, expected at least %d\n",
189 (int)(sizeof(struct nlmsghdr
)
190 +sizeof(struct ifaddrmsg
))));
191 tevent_req_nterror(req
, NT_STATUS_UNEXPECTED_IO_ERROR
);
195 ifa
= (struct ifaddrmsg
*)NLMSG_DATA(h
);
197 state
->addr
.ss_family
= ifa
->ifa_family
;
200 len
= h
->nlmsg_len
- sizeof(struct nlmsghdr
) + sizeof(struct ifaddrmsg
);
204 for (rta
= IFA_RTA(ifa
); RTA_OK(rta
, len
); rta
= RTA_NEXT(rta
, len
)) {
206 if ((rta
->rta_type
!= IFA_LOCAL
)
207 && (rta
->rta_type
!= IFA_ADDRESS
)) {
211 switch (ifa
->ifa_family
) {
213 struct sockaddr_in
*v4_addr
;
214 v4_addr
= (struct sockaddr_in
*)(void *)&state
->addr
;
216 if (RTA_PAYLOAD(rta
) != sizeof(uint32_t)) {
219 v4_addr
->sin_addr
.s_addr
= *(uint32_t *)RTA_DATA(rta
);
224 struct sockaddr_in6
*v6_addr
;
225 v6_addr
= (struct sockaddr_in6
*)(void *)&state
->addr
;
227 if (RTA_PAYLOAD(rta
) !=
228 sizeof(v6_addr
->sin6_addr
.s6_addr
)) {
231 memcpy(v6_addr
->sin6_addr
.s6_addr
, RTA_DATA(rta
),
232 sizeof(v6_addr
->sin6_addr
.s6_addr
));
240 tevent_req_nterror(req
, NT_STATUS_INVALID_ADDRESS
);
244 tevent_req_done(req
);
248 state
->fromaddr_len
= sizeof(state
->fromaddr
);
249 subreq
= recvfrom_send(state
, state
->ev
, state
->ctx
->sock
,
250 state
->buf
, sizeof(state
->buf
), 0,
251 &state
->fromaddr
, &state
->fromaddr_len
);
252 if (tevent_req_nomem(subreq
, req
)) {
255 tevent_req_set_callback(subreq
, addrchange_done
, req
);
258 NTSTATUS
addrchange_recv(struct tevent_req
*req
, enum addrchange_type
*type
,
259 struct sockaddr_storage
*addr
)
261 struct addrchange_state
*state
= tevent_req_data(
262 req
, struct addrchange_state
);
265 if (tevent_req_is_nterror(req
, &status
)) {
276 NTSTATUS
addrchange_context_create(TALLOC_CTX
*mem_ctx
,
277 struct addrchange_context
**pctx
)
279 return NT_STATUS_NOT_SUPPORTED
;
282 struct tevent_req
*addrchange_send(TALLOC_CTX
*mem_ctx
,
283 struct tevent_context
*ev
,
284 struct addrchange_context
*ctx
)
289 NTSTATUS
addrchange_recv(struct tevent_req
*req
, enum addrchange_type
*type
,
290 struct sockaddr_storage
*addr
)
292 return NT_STATUS_NOT_IMPLEMENTED
;