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