1 /* dnsmasq is Copyright (c) 2000-2006 Simon Kelley 2 3 This program is free software; you can redistribute it and/or modify 4 it under the terms of the GNU General Public License as published by 5 the Free Software Foundation; version 2 dated June, 1991. 6 7 This program is distributed in the hope that it will be useful, 8 but WITHOUT ANY WARRANTY; without even the implied warranty of 9 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 GNU General Public License for more details. 11*/ 12 13#include "dnsmasq.h" 14 15#ifdef HAVE_LINUX_NETWORK 16 17#include <linux/types.h> 18#include <linux/netlink.h> 19#include <linux/rtnetlink.h> 20 21static struct iovec iov; 22 23static void nl_err(struct nlmsghdr *h); 24static void nl_routechange(struct daemon *daemon, struct nlmsghdr *h); 25 26void netlink_init(struct daemon *daemon) 27{ 28 struct sockaddr_nl addr; 29 30 addr.nl_family = AF_NETLINK; 31 addr.nl_pad = 0; 32 addr.nl_pid = 0; /* autobind */ 33#ifdef HAVE_IPV6 34 addr.nl_groups = RTMGRP_IPV4_ROUTE | RTMGRP_IPV6_ROUTE; 35#else 36 addr.nl_groups = RTMGRP_IPV4_ROUTE; 37#endif 38 39 /* May not be able to have permission to set multicast groups don't die in that case */ 40 if ((daemon->netlinkfd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) != -1) 41 { 42 if (bind(daemon->netlinkfd, (struct sockaddr *)&addr, sizeof(addr)) == -1) 43 { 44 addr.nl_groups = 0; 45 if (errno != EPERM || bind(daemon->netlinkfd, (struct sockaddr *)&addr, sizeof(addr)) == -1) 46 daemon->netlinkfd = -1; 47 } 48 } 49 50 if (daemon->netlinkfd == -1) 51 die(_("cannot create netlink socket: %s"), NULL); 52 else 53 { 54 int flags = fcntl(daemon->netlinkfd, F_GETFD); 55 if (flags != -1) 56 fcntl(daemon->netlinkfd, F_SETFD, flags | FD_CLOEXEC); 57 } 58 59 iov.iov_len = 200; 60 iov.iov_base = safe_malloc(iov.iov_len); 61} 62 63static ssize_t netlink_recv(struct daemon *daemon) 64{ 65 struct msghdr msg; 66 ssize_t rc; 67 68 msg.msg_control = NULL; 69 msg.msg_controllen = 0; 70 msg.msg_name = NULL; 71 msg.msg_namelen = 0; 72 msg.msg_iov = &iov; 73 msg.msg_iovlen = 1; 74 75 while (1) 76 { 77 msg.msg_flags = 0; 78 while ((rc = recvmsg(daemon->netlinkfd, &msg, MSG_PEEK)) == -1 && errno == EINTR); 79 80 /* 2.2.x doesn't suport MSG_PEEK at all, returning EOPNOTSUPP, so we just grab a 81 big buffer and pray in that case. */ 82 if (rc == -1 && errno == EOPNOTSUPP) 83 { 84 if (!expand_buf(&iov, 2000)) 85 return -1; 86 break; 87 } 88 89 if (rc == -1 || !(msg.msg_flags & MSG_TRUNC)) 90 break; 91 92 if (!expand_buf(&iov, iov.iov_len + 100)) 93 return -1; 94 } 95 96 /* finally, read it for real */ 97 while ((rc = recvmsg(daemon->netlinkfd, &msg, 0)) == -1 && errno == EINTR); 98 99 return rc; 100} 101 102int iface_enumerate(struct daemon *daemon, void *parm, int (*ipv4_callback)(), int (*ipv6_callback)()) 103{ 104 struct sockaddr_nl addr; 105 struct nlmsghdr *h; 106 ssize_t len; 107 static unsigned int seq = 0; 108 int family = AF_INET; 109 110 struct { 111 struct nlmsghdr nlh; 112 struct rtgenmsg g; 113 } req; 114 115 addr.nl_family = AF_NETLINK; 116 addr.nl_pad = 0; 117 addr.nl_groups = 0; 118 addr.nl_pid = 0; /* address to kernel */ 119 120 again: 121 req.nlh.nlmsg_len = sizeof(req); 122 req.nlh.nlmsg_type = RTM_GETADDR; 123 req.nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST | NLM_F_ACK; 124 req.nlh.nlmsg_pid = 0; 125 req.nlh.nlmsg_seq = ++seq; 126 req.g.rtgen_family = family; 127 128 /* Don't block in recvfrom if send fails */ 129 while((len = sendto(daemon->netlinkfd, (void *)&req, sizeof(req), 0, 130 (struct sockaddr *)&addr, sizeof(addr))) == -1 && retry_send()); 131 132 if (len == -1) 133 return 0; 134 135 while (1) 136 { 137 if ((len = netlink_recv(daemon)) == -1) 138 return 0; 139 140 for (h = (struct nlmsghdr *)iov.iov_base; NLMSG_OK(h, (size_t)len); h = NLMSG_NEXT(h, len)) 141 if (h->nlmsg_type == NLMSG_ERROR) 142 nl_err(h); 143 else if (h->nlmsg_seq != seq) 144 nl_routechange(daemon, h); /* May be multicast arriving async */ 145 else if (h->nlmsg_type == NLMSG_DONE) 146 { 147#ifdef HAVE_IPV6 148 if (family == AF_INET && ipv6_callback) 149 { 150 family = AF_INET6; 151 goto again; 152 } 153#endif 154 return 1; 155 } 156 else if (h->nlmsg_type == RTM_NEWADDR) 157 { 158 struct ifaddrmsg *ifa = NLMSG_DATA(h); 159 struct rtattr *rta = IFA_RTA(ifa); 160 unsigned int len1 = h->nlmsg_len - NLMSG_LENGTH(sizeof(*ifa)); 161 162 if (ifa->ifa_family == AF_INET) 163 { 164 struct in_addr netmask, addr, broadcast; 165 166 netmask.s_addr = htonl(0xffffffff << (32 - ifa->ifa_prefixlen)); 167 addr.s_addr = 0; 168 broadcast.s_addr = 0; 169 170 while (RTA_OK(rta, len1)) 171 { 172 if (rta->rta_type == IFA_LOCAL) 173 addr = *((struct in_addr *)(rta+1)); 174 else if (rta->rta_type == IFA_BROADCAST) 175 broadcast = *((struct in_addr *)(rta+1)); 176 177 rta = RTA_NEXT(rta, len1); 178 } 179 180 if (addr.s_addr && ipv4_callback) 181 if (!((*ipv4_callback)(daemon, addr, ifa->ifa_index, netmask, broadcast, parm))) 182 return 0; 183 } 184#ifdef HAVE_IPV6 185 else if (ifa->ifa_family == AF_INET6) 186 { 187 struct in6_addr *addrp = NULL; 188 while (RTA_OK(rta, len1)) 189 { 190 if (rta->rta_type == IFA_ADDRESS) 191 addrp = ((struct in6_addr *)(rta+1)); 192 193 rta = RTA_NEXT(rta, len1); 194 } 195 196 if (addrp && ipv6_callback) 197 if (!((*ipv6_callback)(daemon, addrp, ifa->ifa_index, ifa->ifa_index, parm))) 198 return 0; 199 } 200#endif 201 } 202 } 203} 204 205void netlink_multicast(struct daemon *daemon) 206{ 207 ssize_t len; 208 struct nlmsghdr *h; 209 210 if ((len = netlink_recv(daemon)) != -1) 211 { 212 for (h = (struct nlmsghdr *)iov.iov_base; NLMSG_OK(h, (size_t)len); h = NLMSG_NEXT(h, len)) 213 if (h->nlmsg_type == NLMSG_ERROR) 214 nl_err(h); 215 else 216 nl_routechange(daemon, h); 217 } 218} 219 220static void nl_err(struct nlmsghdr *h) 221{ 222 struct nlmsgerr *err = NLMSG_DATA(h); 223 if (err->error != 0) 224 syslog(LOG_ERR, _("netlink returns error: %s"), strerror(-(err->error))); 225} 226 227/* We arrange to receive netlink multicast messages whenever the network route is added. 228 If this happens and we still have a DNS packet in the buffer, we re-send it. 229 This helps on DoD links, where frequently the packet which triggers dialling is 230 a DNS query, which then gets lost. By re-sending, we can avoid the lookup 231 failing. */ 232static void nl_routechange(struct daemon *daemon, struct nlmsghdr *h) 233{ 234 if (h->nlmsg_type == RTM_NEWROUTE && daemon->srv_save) 235 { 236 struct rtmsg *rtm = NLMSG_DATA(h); 237 if (rtm->rtm_type == RTN_UNICAST && 238 rtm->rtm_scope == RT_SCOPE_LINK) 239 while(sendto(daemon->srv_save->sfd->fd, daemon->packet, daemon->packet_len, 0, 240 &daemon->srv_save->addr.sa, sa_len(&daemon->srv_save->addr)) == -1 && retry_send()); 241 } 242} 243#endif 244 245 246