LCOV - code coverage report
Current view: top level - source3/lib - addrchange.c (source / functions) Hit Total Coverage
Test: coverage report for master 2f515e9b Lines: 28 152 18.4 %
Date: 2024-04-21 15:09:00 Functions: 2 4 50.0 %

          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

Generated by: LCOV version 1.14