Line data Source code
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/netlink.h"
27 : #include "linux/rtnetlink.h"
28 : #include "lib/tsocket/tsocket.h"
29 :
30 : struct addrchange_context {
31 : struct tdgram_context *sock;
32 : };
33 :
34 45 : NTSTATUS addrchange_context_create(TALLOC_CTX *mem_ctx,
35 : struct addrchange_context **pctx)
36 : {
37 0 : struct addrchange_context *ctx;
38 0 : struct sockaddr_nl addr;
39 0 : NTSTATUS status;
40 45 : int sock = -1;
41 0 : int res;
42 0 : bool ok;
43 :
44 45 : ctx = talloc(mem_ctx, struct addrchange_context);
45 45 : if (ctx == NULL) {
46 0 : return NT_STATUS_NO_MEMORY;
47 : }
48 :
49 45 : sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
50 45 : if (sock == -1) {
51 0 : status = map_nt_error_from_unix(errno);
52 0 : goto fail;
53 : }
54 :
55 45 : ok = smb_set_close_on_exec(sock);
56 45 : if (!ok) {
57 0 : status = map_nt_error_from_unix(errno);
58 0 : goto fail;
59 : }
60 :
61 45 : res = set_blocking(sock, false);
62 45 : if (res == -1) {
63 0 : status = map_nt_error_from_unix(errno);
64 0 : goto fail;
65 : }
66 :
67 : /*
68 : * We're interested in address changes
69 : */
70 45 : ZERO_STRUCT(addr);
71 45 : addr.nl_family = AF_NETLINK;
72 45 : addr.nl_groups = RTMGRP_IPV6_IFADDR | RTMGRP_IPV4_IFADDR;
73 :
74 45 : res = bind(sock, (struct sockaddr *)(void *)&addr, sizeof(addr));
75 45 : if (res == -1) {
76 0 : status = map_nt_error_from_unix(errno);
77 0 : goto fail;
78 : }
79 :
80 45 : res = tdgram_bsd_existing_socket(ctx, sock, &ctx->sock);
81 45 : if (res == -1) {
82 0 : status = map_nt_error_from_unix(errno);
83 0 : goto fail;
84 : }
85 :
86 45 : *pctx = ctx;
87 45 : return NT_STATUS_OK;
88 0 : fail:
89 0 : if (sock != -1) {
90 0 : close(sock);
91 : }
92 0 : TALLOC_FREE(ctx);
93 0 : return status;
94 : }
95 :
96 : struct addrchange_state {
97 : struct tevent_context *ev;
98 : struct addrchange_context *ctx;
99 : uint8_t *buf;
100 : struct tsocket_address *fromaddr;
101 :
102 : enum addrchange_type type;
103 : struct sockaddr_storage addr;
104 : uint32_t if_index;
105 : };
106 :
107 : static void addrchange_done(struct tevent_req *subreq);
108 :
109 45 : struct tevent_req *addrchange_send(TALLOC_CTX *mem_ctx,
110 : struct tevent_context *ev,
111 : struct addrchange_context *ctx)
112 : {
113 0 : struct tevent_req *req, *subreq;
114 0 : struct addrchange_state *state;
115 :
116 45 : req = tevent_req_create(mem_ctx, &state, struct addrchange_state);
117 45 : if (req == NULL) {
118 0 : return NULL;
119 : }
120 45 : state->ev = ev;
121 45 : state->ctx = ctx;
122 :
123 45 : subreq = tdgram_recvfrom_send(state, state->ev, state->ctx->sock);
124 45 : if (tevent_req_nomem(subreq, req)) {
125 0 : return tevent_req_post(req, state->ev);
126 : }
127 45 : tevent_req_set_callback(subreq, addrchange_done, req);
128 45 : return req;
129 : }
130 :
131 0 : static void addrchange_done(struct tevent_req *subreq)
132 : {
133 0 : struct tevent_req *req = tevent_req_callback_data(
134 : subreq, struct tevent_req);
135 0 : struct addrchange_state *state = tevent_req_data(
136 : req, struct addrchange_state);
137 0 : union {
138 : struct sockaddr sa;
139 : struct sockaddr_nl nl;
140 : struct sockaddr_storage ss;
141 : } fromaddr;
142 0 : struct nlmsghdr *h;
143 0 : struct ifaddrmsg *ifa;
144 0 : struct rtattr *rta;
145 0 : ssize_t received;
146 0 : int len;
147 0 : int err;
148 0 : bool found;
149 :
150 0 : received = tdgram_recvfrom_recv(subreq, &err, state,
151 : &state->buf,
152 : &state->fromaddr);
153 0 : TALLOC_FREE(subreq);
154 0 : if (received == -1) {
155 0 : DEBUG(10, ("tdgram_recvfrom_recv returned %s\n", strerror(err)));
156 0 : tevent_req_nterror(req, map_nt_error_from_unix(err));
157 0 : return;
158 : }
159 0 : len = tsocket_address_bsd_sockaddr(state->fromaddr,
160 : &fromaddr.sa,
161 : sizeof(fromaddr));
162 :
163 0 : if ((len != sizeof(fromaddr.nl) ||
164 0 : fromaddr.sa.sa_family != AF_NETLINK))
165 : {
166 0 : DEBUG(10, ("Got message from wrong addr\n"));
167 0 : goto retry;
168 : }
169 :
170 0 : if (fromaddr.nl.nl_pid != 0) {
171 0 : DEBUG(10, ("Got msg from pid %d, not from the kernel\n",
172 : (int)fromaddr.nl.nl_pid));
173 0 : goto retry;
174 : }
175 :
176 0 : if (received < sizeof(struct nlmsghdr)) {
177 0 : DEBUG(10, ("received %d, expected at least %d\n",
178 : (int)received, (int)sizeof(struct nlmsghdr)));
179 0 : goto retry;
180 : }
181 :
182 0 : h = (struct nlmsghdr *)state->buf;
183 0 : if (h->nlmsg_len < sizeof(struct nlmsghdr)) {
184 0 : DEBUG(10, ("nlmsg_len=%d, expected at least %d\n",
185 : (int)h->nlmsg_len, (int)sizeof(struct nlmsghdr)));
186 0 : goto retry;
187 : }
188 0 : if (h->nlmsg_len > received) {
189 0 : DEBUG(10, ("nlmsg_len=%d, expected at most %d\n",
190 : (int)h->nlmsg_len, (int)received));
191 0 : goto retry;
192 : }
193 0 : switch (h->nlmsg_type) {
194 0 : case RTM_NEWADDR:
195 0 : state->type = ADDRCHANGE_ADD;
196 0 : break;
197 0 : case RTM_DELADDR:
198 0 : state->type = ADDRCHANGE_DEL;
199 0 : break;
200 0 : default:
201 0 : DEBUG(10, ("Got unexpected type %d - ignoring\n", h->nlmsg_type));
202 0 : goto retry;
203 : }
204 :
205 0 : if (h->nlmsg_len < sizeof(struct nlmsghdr)+sizeof(struct ifaddrmsg)) {
206 0 : DEBUG(10, ("nlmsg_len=%d, expected at least %d\n",
207 : (int)h->nlmsg_len,
208 : (int)(sizeof(struct nlmsghdr)
209 : +sizeof(struct ifaddrmsg))));
210 0 : tevent_req_nterror(req, NT_STATUS_UNEXPECTED_IO_ERROR);
211 0 : return;
212 : }
213 :
214 0 : ifa = (struct ifaddrmsg *)NLMSG_DATA(h);
215 :
216 0 : state->addr.ss_family = ifa->ifa_family;
217 0 : state->if_index = ifa->ifa_index;
218 :
219 0 : len = h->nlmsg_len - sizeof(struct nlmsghdr) + sizeof(struct ifaddrmsg);
220 :
221 0 : found = false;
222 :
223 0 : for (rta = IFA_RTA(ifa); RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) {
224 :
225 0 : if ((rta->rta_type != IFA_LOCAL)
226 0 : && (rta->rta_type != IFA_ADDRESS)) {
227 0 : continue;
228 : }
229 :
230 0 : switch (ifa->ifa_family) {
231 0 : case AF_INET: {
232 0 : struct sockaddr_in *v4_addr;
233 0 : v4_addr = (struct sockaddr_in *)(void *)&state->addr;
234 :
235 0 : if (RTA_PAYLOAD(rta) != sizeof(uint32_t)) {
236 0 : continue;
237 : }
238 0 : v4_addr->sin_addr.s_addr = *(uint32_t *)RTA_DATA(rta);
239 0 : found = true;
240 0 : break;
241 : }
242 0 : case AF_INET6: {
243 0 : struct sockaddr_in6 *v6_addr;
244 0 : v6_addr = (struct sockaddr_in6 *)(void *)&state->addr;
245 :
246 0 : if (RTA_PAYLOAD(rta) !=
247 : sizeof(v6_addr->sin6_addr.s6_addr)) {
248 0 : continue;
249 : }
250 0 : memcpy(v6_addr->sin6_addr.s6_addr, RTA_DATA(rta),
251 : sizeof(v6_addr->sin6_addr.s6_addr));
252 0 : found = true;
253 0 : break;
254 : }
255 : }
256 : }
257 :
258 0 : if (!found) {
259 0 : tevent_req_nterror(req, NT_STATUS_INVALID_ADDRESS);
260 0 : return;
261 : }
262 :
263 0 : tevent_req_done(req);
264 0 : return;
265 :
266 0 : retry:
267 0 : TALLOC_FREE(state->buf);
268 0 : TALLOC_FREE(state->fromaddr);
269 :
270 0 : subreq = tdgram_recvfrom_send(state, state->ev, state->ctx->sock);
271 0 : if (tevent_req_nomem(subreq, req)) {
272 0 : return;
273 : }
274 0 : tevent_req_set_callback(subreq, addrchange_done, req);
275 : }
276 :
277 0 : NTSTATUS addrchange_recv(struct tevent_req *req,
278 : enum addrchange_type *type,
279 : struct sockaddr_storage *addr,
280 : uint32_t *if_index)
281 : {
282 0 : struct addrchange_state *state = tevent_req_data(
283 : req, struct addrchange_state);
284 0 : NTSTATUS status;
285 :
286 0 : if (tevent_req_is_nterror(req, &status)) {
287 0 : tevent_req_received(req);
288 0 : return status;
289 : }
290 :
291 0 : *type = state->type;
292 0 : *addr = state->addr;
293 0 : if (if_index != NULL) {
294 0 : *if_index = state->if_index;
295 : }
296 0 : tevent_req_received(req);
297 0 : return NT_STATUS_OK;
298 : }
299 :
300 : #else
301 :
302 : NTSTATUS addrchange_context_create(TALLOC_CTX *mem_ctx,
303 : struct addrchange_context **pctx)
304 : {
305 : return NT_STATUS_NOT_SUPPORTED;
306 : }
307 :
308 : struct tevent_req *addrchange_send(TALLOC_CTX *mem_ctx,
309 : struct tevent_context *ev,
310 : struct addrchange_context *ctx)
311 : {
312 : return NULL;
313 : }
314 :
315 : NTSTATUS addrchange_recv(struct tevent_req *req, enum addrchange_type *type,
316 : struct sockaddr_storage *addr, uint32_t *if_index)
317 : {
318 : return NT_STATUS_NOT_IMPLEMENTED;
319 : }
320 :
321 : #endif
|