version 1.1.1.4, 2021/03/17 00:56:46
|
version 1.1.1.5, 2023/09/27 11:02:07
|
Line 1
|
Line 1
|
/* dnsmasq is Copyright (c) 2000-2021 Simon Kelley | /* dnsmasq is Copyright (c) 2000-2022 Simon Kelley |
|
|
This program is free software; you can redistribute it and/or modify |
This program is free software; you can redistribute it and/or modify |
it under the terms of the GNU General Public License as published by |
it under the terms of the GNU General Public License as published by |
Line 41
|
Line 41
|
|
|
#ifndef NDA_RTA |
#ifndef NDA_RTA |
# define NDA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ndmsg)))) |
# define NDA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ndmsg)))) |
#endif | #endif |
|
|
|
/* Used to request refresh of addresses or routes just once, |
|
* when multiple changes might be announced. */ |
|
enum async_states { |
|
STATE_NEWADDR = (1 << 0), |
|
STATE_NEWROUTE = (1 << 1), |
|
}; |
|
|
|
|
static struct iovec iov; |
static struct iovec iov; |
static u32 netlink_pid; |
static u32 netlink_pid; |
|
|
static void nl_async(struct nlmsghdr *h); | static unsigned nl_async(struct nlmsghdr *h, unsigned state); |
| static void nl_multicast_state(unsigned state); |
|
|
char *netlink_init(void) |
char *netlink_init(void) |
{ |
{ |
struct sockaddr_nl addr; |
struct sockaddr_nl addr; |
socklen_t slen = sizeof(addr); |
socklen_t slen = sizeof(addr); |
int opt = 1; |
|
|
|
addr.nl_family = AF_NETLINK; |
addr.nl_family = AF_NETLINK; |
addr.nl_pad = 0; |
addr.nl_pad = 0; |
addr.nl_pid = 0; /* autobind */ |
addr.nl_pid = 0; /* autobind */ |
addr.nl_groups = RTMGRP_IPV4_ROUTE; |
addr.nl_groups = RTMGRP_IPV4_ROUTE; |
if (option_bool(OPT_CLEVERBIND)) | addr.nl_groups |= RTMGRP_IPV4_IFADDR; |
addr.nl_groups |= RTMGRP_IPV4_IFADDR; | |
addr.nl_groups |= RTMGRP_IPV6_ROUTE; |
addr.nl_groups |= RTMGRP_IPV6_ROUTE; |
if (option_bool(OPT_CLEVERBIND)) | addr.nl_groups |= RTMGRP_IPV6_IFADDR; |
addr.nl_groups |= RTMGRP_IPV6_IFADDR; | |
|
|
#ifdef HAVE_DHCP6 |
|
if (daemon->doing_ra || daemon->doing_dhcp6) |
|
addr.nl_groups |= RTMGRP_IPV6_IFADDR; |
|
#endif |
|
|
|
/* May not be able to have permission to set multicast groups don't die in that case */ |
/* May not be able to have permission to set multicast groups don't die in that case */ |
if ((daemon->netlinkfd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) != -1) |
if ((daemon->netlinkfd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) != -1) |
{ |
{ |
Line 92 char *netlink_init(void)
|
Line 92 char *netlink_init(void)
|
iov.iov_len = 100; |
iov.iov_len = 100; |
iov.iov_base = safe_malloc(iov.iov_len); |
iov.iov_base = safe_malloc(iov.iov_len); |
|
|
if (daemon->kernel_version >= KERNEL_VERSION(2,6,30) && |
|
setsockopt(daemon->netlinkfd, SOL_NETLINK, NETLINK_NO_ENOBUFS, &opt, sizeof(opt)) == -1) |
|
return _("warning: failed to set NETLINK_NO_ENOBUFS on netlink socket"); |
|
|
|
return NULL; |
return NULL; |
} |
} |
|
|
static ssize_t netlink_recv(void) | static ssize_t netlink_recv(int flags) |
{ |
{ |
struct msghdr msg; |
struct msghdr msg; |
struct sockaddr_nl nladdr; |
struct sockaddr_nl nladdr; |
Line 115 static ssize_t netlink_recv(void)
|
Line 111 static ssize_t netlink_recv(void)
|
msg.msg_iovlen = 1; |
msg.msg_iovlen = 1; |
msg.msg_flags = 0; |
msg.msg_flags = 0; |
|
|
while ((rc = recvmsg(daemon->netlinkfd, &msg, MSG_PEEK | MSG_TRUNC)) == -1 && errno == EINTR); | while ((rc = recvmsg(daemon->netlinkfd, &msg, flags | MSG_PEEK | MSG_TRUNC)) == -1 && |
| errno == EINTR); |
|
|
/* make buffer big enough */ |
/* make buffer big enough */ |
if (rc != -1 && (msg.msg_flags & MSG_TRUNC)) |
if (rc != -1 && (msg.msg_flags & MSG_TRUNC)) |
Line 132 static ssize_t netlink_recv(void)
|
Line 129 static ssize_t netlink_recv(void)
|
|
|
/* read it for real */ |
/* read it for real */ |
msg.msg_flags = 0; |
msg.msg_flags = 0; |
while ((rc = recvmsg(daemon->netlinkfd, &msg, 0)) == -1 && errno == EINTR); | while ((rc = recvmsg(daemon->netlinkfd, &msg, flags)) == -1 && errno == EINTR); |
|
|
/* Make sure this is from the kernel */ |
/* Make sure this is from the kernel */ |
if (rc == -1 || nladdr.nl_pid == 0) |
if (rc == -1 || nladdr.nl_pid == 0) |
Line 151 static ssize_t netlink_recv(void)
|
Line 148 static ssize_t netlink_recv(void)
|
|
|
|
|
/* family = AF_UNSPEC finds ARP table entries. |
/* family = AF_UNSPEC finds ARP table entries. |
family = AF_LOCAL finds MAC addresses. */ | family = AF_LOCAL finds MAC addresses. |
| returns 0 on failure, 1 on success, -1 when restart is required |
| */ |
int iface_enumerate(int family, void *parm, int (*callback)()) |
int iface_enumerate(int family, void *parm, int (*callback)()) |
{ |
{ |
struct sockaddr_nl addr; |
struct sockaddr_nl addr; |
Line 159 int iface_enumerate(int family, void *parm, int (*call
|
Line 158 int iface_enumerate(int family, void *parm, int (*call
|
ssize_t len; |
ssize_t len; |
static unsigned int seq = 0; |
static unsigned int seq = 0; |
int callback_ok = 1; |
int callback_ok = 1; |
|
unsigned state = 0; |
|
|
struct { |
struct { |
struct nlmsghdr nlh; |
struct nlmsghdr nlh; |
Line 170 int iface_enumerate(int family, void *parm, int (*call
|
Line 170 int iface_enumerate(int family, void *parm, int (*call
|
|
|
addr.nl_family = AF_NETLINK; |
addr.nl_family = AF_NETLINK; |
|
|
again: |
|
if (family == AF_UNSPEC) |
if (family == AF_UNSPEC) |
req.nlh.nlmsg_type = RTM_GETNEIGH; |
req.nlh.nlmsg_type = RTM_GETNEIGH; |
else if (family == AF_LOCAL) |
else if (family == AF_LOCAL) |
Line 193 int iface_enumerate(int family, void *parm, int (*call
|
Line 192 int iface_enumerate(int family, void *parm, int (*call
|
|
|
while (1) |
while (1) |
{ |
{ |
if ((len = netlink_recv()) == -1) | if ((len = netlink_recv(0)) == -1) |
{ |
{ |
if (errno == ENOBUFS) |
if (errno == ENOBUFS) |
{ |
{ |
sleep(1); | nl_multicast_state(state); |
goto again; | return -1; |
} |
} |
return 0; |
return 0; |
} |
} |
Line 207 int iface_enumerate(int family, void *parm, int (*call
|
Line 206 int iface_enumerate(int family, void *parm, int (*call
|
if (h->nlmsg_pid != netlink_pid || h->nlmsg_type == NLMSG_ERROR) |
if (h->nlmsg_pid != netlink_pid || h->nlmsg_type == NLMSG_ERROR) |
{ |
{ |
/* May be multicast arriving async */ |
/* May be multicast arriving async */ |
nl_async(h); | state = nl_async(h, state); |
} |
} |
else if (h->nlmsg_seq != seq) |
else if (h->nlmsg_seq != seq) |
{ |
{ |
Line 259 int iface_enumerate(int family, void *parm, int (*call
|
Line 258 int iface_enumerate(int family, void *parm, int (*call
|
|
|
while (RTA_OK(rta, len1)) |
while (RTA_OK(rta, len1)) |
{ |
{ |
if (rta->rta_type == IFA_ADDRESS) | /* |
| * Important comment: (from if_addr.h) |
| * IFA_ADDRESS is prefix address, rather than local interface address. |
| * It makes no difference for normally configured broadcast interfaces, |
| * but for point-to-point IFA_ADDRESS is DESTINATION address, |
| * local address is supplied in IFA_LOCAL attribute. |
| */ |
| if (rta->rta_type == IFA_LOCAL) |
| addrp = ((struct in6_addr *)(rta+1)); |
| else if (rta->rta_type == IFA_ADDRESS && !addrp) |
addrp = ((struct in6_addr *)(rta+1)); |
addrp = ((struct in6_addr *)(rta+1)); |
else if (rta->rta_type == IFA_CACHEINFO) |
else if (rta->rta_type == IFA_CACHEINFO) |
{ |
{ |
Line 341 int iface_enumerate(int family, void *parm, int (*call
|
Line 349 int iface_enumerate(int family, void *parm, int (*call
|
} |
} |
} |
} |
|
|
void netlink_multicast(void) | static void nl_multicast_state(unsigned state) |
{ |
{ |
ssize_t len; |
ssize_t len; |
struct nlmsghdr *h; |
struct nlmsghdr *h; |
int flags; | |
| do { |
| /* don't risk blocking reading netlink messages here. */ |
| while ((len = netlink_recv(MSG_DONTWAIT)) != -1) |
|
|
/* don't risk blocking reading netlink messages here. */ | for (h = (struct nlmsghdr *)iov.iov_base; NLMSG_OK(h, (size_t)len); h = NLMSG_NEXT(h, len)) |
if ((flags = fcntl(daemon->netlinkfd, F_GETFL)) == -1 || | state = nl_async(h, state); |
fcntl(daemon->netlinkfd, F_SETFL, flags | O_NONBLOCK) == -1) | } while (errno == ENOBUFS); |
return; | |
| |
if ((len = netlink_recv()) != -1) | |
for (h = (struct nlmsghdr *)iov.iov_base; NLMSG_OK(h, (size_t)len); h = NLMSG_NEXT(h, len)) | |
nl_async(h); | |
| |
/* restore non-blocking status */ | |
fcntl(daemon->netlinkfd, F_SETFL, flags); | |
} |
} |
|
|
static void nl_async(struct nlmsghdr *h) | void netlink_multicast(void) |
{ |
{ |
|
unsigned state = 0; |
|
nl_multicast_state(state); |
|
} |
|
|
|
|
|
static unsigned nl_async(struct nlmsghdr *h, unsigned state) |
|
{ |
if (h->nlmsg_type == NLMSG_ERROR) |
if (h->nlmsg_type == NLMSG_ERROR) |
{ |
{ |
struct nlmsgerr *err = NLMSG_DATA(h); |
struct nlmsgerr *err = NLMSG_DATA(h); |
if (err->error != 0) |
if (err->error != 0) |
my_syslog(LOG_ERR, _("netlink returns error: %s"), strerror(-(err->error))); |
my_syslog(LOG_ERR, _("netlink returns error: %s"), strerror(-(err->error))); |
} |
} |
else if (h->nlmsg_pid == 0 && h->nlmsg_type == RTM_NEWROUTE) | else if (h->nlmsg_pid == 0 && h->nlmsg_type == RTM_NEWROUTE && |
| (state & STATE_NEWROUTE)==0) |
{ |
{ |
/* We arrange to receive netlink multicast messages whenever the network route is added. |
/* We arrange to receive netlink multicast messages whenever the network route is added. |
If this happens and we still have a DNS packet in the buffer, we re-send it. |
If this happens and we still have a DNS packet in the buffer, we re-send it. |
Line 380 static void nl_async(struct nlmsghdr *h)
|
Line 391 static void nl_async(struct nlmsghdr *h)
|
if (rtm->rtm_type == RTN_UNICAST && rtm->rtm_scope == RT_SCOPE_LINK && |
if (rtm->rtm_type == RTN_UNICAST && rtm->rtm_scope == RT_SCOPE_LINK && |
(rtm->rtm_table == RT_TABLE_MAIN || |
(rtm->rtm_table == RT_TABLE_MAIN || |
rtm->rtm_table == RT_TABLE_LOCAL)) |
rtm->rtm_table == RT_TABLE_LOCAL)) |
queue_event(EVENT_NEWROUTE); | { |
| queue_event(EVENT_NEWROUTE); |
| state |= STATE_NEWROUTE; |
| } |
} |
} |
else if (h->nlmsg_type == RTM_NEWADDR || h->nlmsg_type == RTM_DELADDR) | else if ((h->nlmsg_type == RTM_NEWADDR || h->nlmsg_type == RTM_DELADDR) && |
queue_event(EVENT_NEWADDR); | (state & STATE_NEWADDR)==0) |
| { |
| queue_event(EVENT_NEWADDR); |
| state |= STATE_NEWADDR; |
| } |
| return state; |
} |
} |
#endif | #endif /* HAVE_LINUX_NETWORK */ |
| |
| |