"Fossies" - the Fresh Open Source Software Archive  

Source code changes of the file "src/netlink.c" between
dnsmasq-2.84.tar.xz and dnsmasq-2.85.tar.xz

About: Dnsmasq is a lightweight caching DNS forwarder and DHCP server.

netlink.c  (dnsmasq-2.84.tar.xz):netlink.c  (dnsmasq-2.85.tar.xz)
skipping to change at line 46 skipping to change at line 46
# define IFA_RTA(r) \ # define IFA_RTA(r) \
((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifaddrmsg)))) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifaddrmsg))))
# include <linux/if_addr.h> # include <linux/if_addr.h>
#endif #endif
#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)) 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)) if (option_bool(OPT_CLEVERBIND))
addr.nl_groups |= RTMGRP_IPV6_IFADDR; addr.nl_groups |= RTMGRP_IPV6_IFADDR;
skipping to change at line 93 skipping to change at line 100
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);
if (daemon->kernel_version >= KERNEL_VERSION(2,6,30) &&
setsockopt(daemon->netlinkfd, SOL_NETLINK, NETLINK_NO_ENOBUFS, &opt, sizeo
f(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;
ssize_t rc; ssize_t rc;
while (1) while (1)
{ {
msg.msg_control = NULL; msg.msg_control = NULL;
msg.msg_controllen = 0; msg.msg_controllen = 0;
msg.msg_name = &nladdr; msg.msg_name = &nladdr;
msg.msg_namelen = sizeof(nladdr); msg.msg_namelen = sizeof(nladdr);
msg.msg_iov = &iov; msg.msg_iov = &iov;
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 while ((rc = recvmsg(daemon->netlinkfd, &msg, flags | MSG_PEEK | MSG_TRUNC
&& errno == EINTR); )) == -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))
{ {
/* Very new Linux kernels return the actual size needed, older ones alw ays return truncated size */ /* Very new Linux kernels return the actual size needed, older ones alw ays return truncated size */
if ((size_t)rc == iov.iov_len) if ((size_t)rc == iov.iov_len)
{ {
if (expand_buf(&iov, rc + 100)) if (expand_buf(&iov, rc + 100))
continue; continue;
} }
else else
expand_buf(&iov, rc); expand_buf(&iov, rc);
} }
/* 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 == EI NTR);
/* 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)
break; break;
} }
/* discard stuff which is truncated at this point (expand_buf() may fail) */ /* discard stuff which is truncated at this point (expand_buf() may fail) */
if (msg.msg_flags & MSG_TRUNC) if (msg.msg_flags & MSG_TRUNC)
{ {
rc = -1; rc = -1;
errno = ENOMEM; errno = ENOMEM;
} }
return rc; return rc;
} }
/* 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;
struct nlmsghdr *h; struct nlmsghdr *h;
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(&req, 0, sizeof(req));
memset(&addr, 0, sizeof(addr)); memset(&addr, 0, sizeof(addr));
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)
req.nlh.nlmsg_type = RTM_GETLINK; req.nlh.nlmsg_type = RTM_GETLINK;
else else
req.nlh.nlmsg_type = RTM_GETADDR; req.nlh.nlmsg_type = RTM_GETADDR;
req.nlh.nlmsg_len = sizeof(req); req.nlh.nlmsg_len = sizeof(req);
req.nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST | NLM_F_ACK; req.nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST | NLM_F_ACK;
req.nlh.nlmsg_pid = 0; req.nlh.nlmsg_pid = 0;
skipping to change at line 193 skipping to change at line 199
/* Don't block in recvfrom if send fails */ /* Don't block in recvfrom if send fails */
while(retry_send(sendto(daemon->netlinkfd, (void *)&req, sizeof(req), 0, while(retry_send(sendto(daemon->netlinkfd, (void *)&req, sizeof(req), 0,
(struct sockaddr *)&addr, sizeof(addr)))); (struct sockaddr *)&addr, sizeof(addr))));
if (errno != 0) if (errno != 0)
return 0; return 0;
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 = NL MSG_NEXT(h, len)) for (h = (struct nlmsghdr *)iov.iov_base; NLMSG_OK(h, (size_t)len); h = NL MSG_NEXT(h, len))
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)
{ {
/* May be part of incomplete response to previous request after /* May be part of incomplete response to previous request after
ENOBUFS. Drop it. */ ENOBUFS. Drop it. */
continue; 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)
skipping to change at line 341 skipping to change at line 347
} }
if (mac && callback_ok && !((link->ifi_flags & (IFF_LOOPBACK | IFF_PO INTOPOINT))) && if (mac && callback_ok && !((link->ifi_flags & (IFF_LOOPBACK | IFF_PO INTOPOINT))) &&
!((*callback)((int)link->ifi_index, (unsigned int)link->ifi_type, mac, maclen, parm))) !((*callback)((int)link->ifi_index, (unsigned int)link->ifi_type, mac, maclen, parm)))
callback_ok = 0; callback_ok = 0;
} }
#endif #endif
} }
} }
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;
/* don't risk blocking reading netlink messages here. */ do {
if ((flags = fcntl(daemon->netlinkfd, F_GETFL)) == -1 || /* don't risk blocking reading netlink messages here. */
fcntl(daemon->netlinkfd, F_SETFL, flags | O_NONBLOCK) == -1) while ((len = netlink_recv(MSG_DONTWAIT)) != -1)
return;
for (h = (struct nlmsghdr *)iov.iov_base; NLMSG_OK(h, (size_t)len); h = NL
if ((len = netlink_recv()) != -1) MSG_NEXT(h, len))
for (h = (struct nlmsghdr *)iov.iov_base; NLMSG_OK(h, (size_t)len); h = NLMS state = nl_async(h, state);
G_NEXT(h, len)) } while (errno == ENOBUFS);
nl_async(h); }
/* restore non-blocking status */ void netlink_multicast(void)
fcntl(daemon->netlinkfd, F_SETFL, flags); {
unsigned state = 0;
nl_multicast_state(state);
} }
static void nl_async(struct nlmsghdr *h) 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 r oute is added. /* We arrange to receive netlink multicast messages whenever the network r oute 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.
This helps on DoD links, where frequently the packet which triggers dial ling is This helps on DoD links, where frequently the packet which triggers dial ling is
a DNS query, which then gets lost. By re-sending, we can avoid the looku p a DNS query, which then gets lost. By re-sending, we can avoid the looku p
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 &&
(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) &&
(state & STATE_NEWADDR)==0)
{
queue_event(EVENT_NEWADDR);
state |= STATE_NEWADDR;
} }
else if (h->nlmsg_type == RTM_NEWADDR || h->nlmsg_type == RTM_DELADDR) return state;
queue_event(EVENT_NEWADDR);
} }
#endif #endif
 End of changes. 21 change blocks. 
35 lines changed or deleted 50 lines changed or added

Home  |  About  |  Features  |  All  |  Newest  |  Dox  |  Diffs  |  RSS Feeds  |  Screenshots  |  Comments  |  Imprint  |  Privacy  |  HTTP(S)