version 1.1.1.3, 2016/11/02 09:57:01
|
version 1.1.1.5, 2023/09/27 11:02:07
|
Line 1
|
Line 1
|
/* dnsmasq is Copyright (c) 2000-2016 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 22
|
Line 22
|
#include <linux/netlink.h> |
#include <linux/netlink.h> |
#include <linux/rtnetlink.h> |
#include <linux/rtnetlink.h> |
|
|
|
/* Blergh. Radv does this, so that's our excuse. */ |
|
#ifndef SOL_NETLINK |
|
#define SOL_NETLINK 270 |
|
#endif |
|
|
|
#ifndef NETLINK_NO_ENOBUFS |
|
#define NETLINK_NO_ENOBUFS 5 |
|
#endif |
|
|
/* linux 2.6.19 buggers up the headers, patch it up here. */ |
/* linux 2.6.19 buggers up the headers, patch it up here. */ |
#ifndef IFA_RTA |
#ifndef IFA_RTA |
# define IFA_RTA(r) \ |
# define IFA_RTA(r) \ |
Line 32
|
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); |
|
|
void 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); |
Line 49 void netlink_init(void)
|
Line 66 void netlink_init(void)
|
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; | |
#ifdef HAVE_IPV6 | |
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; | |
#endif | |
#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 73 void netlink_init(void)
|
Line 82 void netlink_init(void)
|
} |
} |
|
|
if (daemon->netlinkfd == -1 || |
if (daemon->netlinkfd == -1 || |
getsockname(daemon->netlinkfd, (struct sockaddr *)&addr, &slen) == 1) | getsockname(daemon->netlinkfd, (struct sockaddr *)&addr, &slen) == -1) |
die(_("cannot create netlink socket: %s"), NULL, EC_MISC); |
die(_("cannot create netlink socket: %s"), NULL, EC_MISC); |
| |
| |
/* save pid assigned by bind() and retrieved by getsockname() */ |
/* save pid assigned by bind() and retrieved by getsockname() */ |
netlink_pid = addr.nl_pid; |
netlink_pid = addr.nl_pid; |
|
|
iov.iov_len = 100; |
iov.iov_len = 100; |
iov.iov_base = safe_malloc(iov.iov_len); |
iov.iov_base = safe_malloc(iov.iov_len); |
|
|
|
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 99 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 116 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 135 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 143 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; |
struct rtgenmsg g; |
struct rtgenmsg g; |
} req; |
} req; |
|
|
|
memset(&req, 0, sizeof(req)); |
|
memset(&addr, 0, sizeof(addr)); |
|
|
addr.nl_family = AF_NETLINK; |
addr.nl_family = AF_NETLINK; |
addr.nl_pad = 0; |
|
addr.nl_groups = 0; |
|
addr.nl_pid = 0; /* address to kernel */ |
|
|
|
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 177 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; |
} |
} |
|
|
for (h = (struct nlmsghdr *)iov.iov_base; NLMSG_OK(h, (size_t)len); h = NLMSG_NEXT(h, len)) |
for (h = (struct nlmsghdr *)iov.iov_base; NLMSG_OK(h, (size_t)len); h = NLMSG_NEXT(h, len)) |
if (h->nlmsg_seq != seq || 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) |
|
{ |
|
/* May be part of incomplete response to previous request after |
|
ENOBUFS. Drop it. */ |
|
continue; |
|
} |
else if (h->nlmsg_type == NLMSG_DONE) |
else if (h->nlmsg_type == NLMSG_DONE) |
return callback_ok; |
return callback_ok; |
else if (h->nlmsg_type == RTM_NEWADDR && family != AF_UNSPEC && family != AF_LOCAL) |
else if (h->nlmsg_type == RTM_NEWADDR && family != AF_UNSPEC && family != AF_LOCAL) |
Line 229 int iface_enumerate(int family, void *parm, int (*call
|
Line 250 int iface_enumerate(int family, void *parm, int (*call
|
if (!((*callback)(addr, ifa->ifa_index, label, netmask, broadcast, parm))) |
if (!((*callback)(addr, ifa->ifa_index, label, netmask, broadcast, parm))) |
callback_ok = 0; |
callback_ok = 0; |
} |
} |
#ifdef HAVE_IPV6 |
|
else if (ifa->ifa_family == AF_INET6) |
else if (ifa->ifa_family == AF_INET6) |
{ |
{ |
struct in6_addr *addrp = NULL; |
struct in6_addr *addrp = NULL; |
Line 238 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 264 int iface_enumerate(int family, void *parm, int (*call
|
Line 293 int iface_enumerate(int family, void *parm, int (*call
|
(int) preferred, (int)valid, parm))) |
(int) preferred, (int)valid, parm))) |
callback_ok = 0; |
callback_ok = 0; |
} |
} |
#endif |
|
} |
} |
} |
} |
else if (h->nlmsg_type == RTM_NEWNEIGH && family == AF_UNSPEC) |
else if (h->nlmsg_type == RTM_NEWNEIGH && family == AF_UNSPEC) |
Line 321 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 357 static void nl_async(struct nlmsghdr *h)
|
Line 388 static void nl_async(struct nlmsghdr *h)
|
failing. */ |
failing. */ |
struct rtmsg *rtm = NLMSG_DATA(h); |
struct rtmsg *rtm = NLMSG_DATA(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 && |
queue_event(EVENT_NEWROUTE); | (rtm->rtm_table == RT_TABLE_MAIN || |
| rtm->rtm_table == RT_TABLE_LOCAL)) |
| { |
| 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 */ |
| |
| |