Annotation of embedaddon/dnsmasq/src/netlink.c, revision 1.1
1.1 ! misho 1: /* dnsmasq is Copyright (c) 2000-2013 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, or
! 6: (at your option) version 3 dated 29 June, 2007.
! 7:
! 8: This program is distributed in the hope that it will be useful,
! 9: but WITHOUT ANY WARRANTY; without even the implied warranty of
! 10: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
! 11: GNU General Public License for more details.
! 12:
! 13: You should have received a copy of the GNU General Public License
! 14: along with this program. If not, see <http://www.gnu.org/licenses/>.
! 15: */
! 16:
! 17: #include "dnsmasq.h"
! 18:
! 19: #ifdef HAVE_LINUX_NETWORK
! 20:
! 21: #include <linux/types.h>
! 22: #include <linux/netlink.h>
! 23: #include <linux/rtnetlink.h>
! 24:
! 25: /* linux 2.6.19 buggers up the headers, patch it up here. */
! 26: #ifndef IFA_RTA
! 27: # define IFA_RTA(r) \
! 28: ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifaddrmsg))))
! 29:
! 30: # include <linux/if_addr.h>
! 31: #endif
! 32:
! 33: #ifndef NDA_RTA
! 34: # define NDA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ndmsg))))
! 35: #endif
! 36:
! 37:
! 38: static struct iovec iov;
! 39: static u32 netlink_pid;
! 40:
! 41: static int nl_async(struct nlmsghdr *h);
! 42: static void nl_newaddress(time_t now);
! 43:
! 44: void netlink_init(void)
! 45: {
! 46: struct sockaddr_nl addr;
! 47: socklen_t slen = sizeof(addr);
! 48:
! 49: addr.nl_family = AF_NETLINK;
! 50: addr.nl_pad = 0;
! 51: addr.nl_pid = 0; /* autobind */
! 52: addr.nl_groups = RTMGRP_IPV4_ROUTE;
! 53: if (option_bool(OPT_CLEVERBIND))
! 54: addr.nl_groups |= RTMGRP_IPV4_IFADDR;
! 55: #ifdef HAVE_IPV6
! 56: addr.nl_groups |= RTMGRP_IPV6_ROUTE;
! 57: if (option_bool(OPT_CLEVERBIND))
! 58: addr.nl_groups |= RTMGRP_IPV6_IFADDR;
! 59: #endif
! 60: #ifdef HAVE_DHCP6
! 61: if (daemon->doing_ra || daemon->doing_dhcp6)
! 62: addr.nl_groups |= RTMGRP_IPV6_IFADDR;
! 63: #endif
! 64:
! 65: /* May not be able to have permission to set multicast groups don't die in that case */
! 66: if ((daemon->netlinkfd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) != -1)
! 67: {
! 68: if (bind(daemon->netlinkfd, (struct sockaddr *)&addr, sizeof(addr)) == -1)
! 69: {
! 70: addr.nl_groups = 0;
! 71: if (errno != EPERM || bind(daemon->netlinkfd, (struct sockaddr *)&addr, sizeof(addr)) == -1)
! 72: daemon->netlinkfd = -1;
! 73: }
! 74: }
! 75:
! 76: if (daemon->netlinkfd == -1 ||
! 77: getsockname(daemon->netlinkfd, (struct sockaddr *)&addr, &slen) == 1)
! 78: die(_("cannot create netlink socket: %s"), NULL, EC_MISC);
! 79:
! 80: /* save pid assigned by bind() and retrieved by getsockname() */
! 81: netlink_pid = addr.nl_pid;
! 82:
! 83: iov.iov_len = 100;
! 84: iov.iov_base = safe_malloc(iov.iov_len);
! 85: }
! 86:
! 87: static ssize_t netlink_recv(void)
! 88: {
! 89: struct msghdr msg;
! 90: struct sockaddr_nl nladdr;
! 91: ssize_t rc;
! 92:
! 93: while (1)
! 94: {
! 95: msg.msg_control = NULL;
! 96: msg.msg_controllen = 0;
! 97: msg.msg_name = &nladdr;
! 98: msg.msg_namelen = sizeof(nladdr);
! 99: msg.msg_iov = &iov;
! 100: msg.msg_iovlen = 1;
! 101: msg.msg_flags = 0;
! 102:
! 103: while ((rc = recvmsg(daemon->netlinkfd, &msg, MSG_PEEK | MSG_TRUNC)) == -1 && errno == EINTR);
! 104:
! 105: /* make buffer big enough */
! 106: if (rc != -1 && (msg.msg_flags & MSG_TRUNC))
! 107: {
! 108: /* Very new Linux kernels return the actual size needed, older ones always return truncated size */
! 109: if ((size_t)rc == iov.iov_len)
! 110: {
! 111: if (expand_buf(&iov, rc + 100))
! 112: continue;
! 113: }
! 114: else
! 115: expand_buf(&iov, rc);
! 116: }
! 117:
! 118: /* read it for real */
! 119: msg.msg_flags = 0;
! 120: while ((rc = recvmsg(daemon->netlinkfd, &msg, 0)) == -1 && errno == EINTR);
! 121:
! 122: /* Make sure this is from the kernel */
! 123: if (rc == -1 || nladdr.nl_pid == 0)
! 124: break;
! 125: }
! 126:
! 127: /* discard stuff which is truncated at this point (expand_buf() may fail) */
! 128: if (msg.msg_flags & MSG_TRUNC)
! 129: {
! 130: rc = -1;
! 131: errno = ENOMEM;
! 132: }
! 133:
! 134: return rc;
! 135: }
! 136:
! 137:
! 138: /* family = AF_UNSPEC finds ARP table entries.
! 139: family = AF_LOCAL finds MAC addresses. */
! 140: int iface_enumerate(int family, void *parm, int (*callback)())
! 141: {
! 142: struct sockaddr_nl addr;
! 143: struct nlmsghdr *h;
! 144: ssize_t len;
! 145: static unsigned int seq = 0;
! 146: int callback_ok = 1, newaddr = 0;
! 147:
! 148: struct {
! 149: struct nlmsghdr nlh;
! 150: struct rtgenmsg g;
! 151: } req;
! 152:
! 153: addr.nl_family = AF_NETLINK;
! 154: addr.nl_pad = 0;
! 155: addr.nl_groups = 0;
! 156: addr.nl_pid = 0; /* address to kernel */
! 157:
! 158: again:
! 159: if (family == AF_UNSPEC)
! 160: req.nlh.nlmsg_type = RTM_GETNEIGH;
! 161: else if (family == AF_LOCAL)
! 162: req.nlh.nlmsg_type = RTM_GETLINK;
! 163: else
! 164: req.nlh.nlmsg_type = RTM_GETADDR;
! 165:
! 166: req.nlh.nlmsg_len = sizeof(req);
! 167: req.nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST | NLM_F_ACK;
! 168: req.nlh.nlmsg_pid = 0;
! 169: req.nlh.nlmsg_seq = ++seq;
! 170: req.g.rtgen_family = family;
! 171:
! 172: /* Don't block in recvfrom if send fails */
! 173: while((len = sendto(daemon->netlinkfd, (void *)&req, sizeof(req), 0,
! 174: (struct sockaddr *)&addr, sizeof(addr))) == -1 && retry_send());
! 175:
! 176: if (len == -1)
! 177: return 0;
! 178:
! 179: while (1)
! 180: {
! 181: if ((len = netlink_recv()) == -1)
! 182: {
! 183: if (errno == ENOBUFS)
! 184: {
! 185: sleep(1);
! 186: goto again;
! 187: }
! 188: return 0;
! 189: }
! 190:
! 191: for (h = (struct nlmsghdr *)iov.iov_base; NLMSG_OK(h, (size_t)len); h = NLMSG_NEXT(h, len))
! 192: if (h->nlmsg_seq != seq || h->nlmsg_pid != netlink_pid || h->nlmsg_type == NLMSG_ERROR)
! 193: {
! 194: /* May be multicast arriving async */
! 195: if (nl_async(h))
! 196: newaddr = 1;
! 197: }
! 198: else if (h->nlmsg_type == NLMSG_DONE)
! 199: {
! 200: /* handle async new interface address arrivals, these have to be done
! 201: after we complete as we're not re-entrant */
! 202: if (newaddr)
! 203: nl_newaddress(dnsmasq_time());
! 204:
! 205: return callback_ok;
! 206: }
! 207: else if (h->nlmsg_type == RTM_NEWADDR && family != AF_UNSPEC && family != AF_LOCAL)
! 208: {
! 209: struct ifaddrmsg *ifa = NLMSG_DATA(h);
! 210: struct rtattr *rta = IFA_RTA(ifa);
! 211: unsigned int len1 = h->nlmsg_len - NLMSG_LENGTH(sizeof(*ifa));
! 212:
! 213: if (ifa->ifa_family == family)
! 214: {
! 215: if (ifa->ifa_family == AF_INET)
! 216: {
! 217: struct in_addr netmask, addr, broadcast;
! 218:
! 219: netmask.s_addr = htonl(0xffffffff << (32 - ifa->ifa_prefixlen));
! 220: addr.s_addr = 0;
! 221: broadcast.s_addr = 0;
! 222:
! 223: while (RTA_OK(rta, len1))
! 224: {
! 225: if (rta->rta_type == IFA_LOCAL)
! 226: addr = *((struct in_addr *)(rta+1));
! 227: else if (rta->rta_type == IFA_BROADCAST)
! 228: broadcast = *((struct in_addr *)(rta+1));
! 229:
! 230: rta = RTA_NEXT(rta, len1);
! 231: }
! 232:
! 233: if (addr.s_addr && callback_ok)
! 234: if (!((*callback)(addr, ifa->ifa_index, netmask, broadcast, parm)))
! 235: callback_ok = 0;
! 236: }
! 237: #ifdef HAVE_IPV6
! 238: else if (ifa->ifa_family == AF_INET6)
! 239: {
! 240: struct in6_addr *addrp = NULL;
! 241: u32 valid = 0, preferred = 0;
! 242: int flags = 0;
! 243:
! 244: while (RTA_OK(rta, len1))
! 245: {
! 246: if (rta->rta_type == IFA_ADDRESS)
! 247: addrp = ((struct in6_addr *)(rta+1));
! 248: else if (rta->rta_type == IFA_CACHEINFO)
! 249: {
! 250: struct ifa_cacheinfo *ifc = (struct ifa_cacheinfo *)(rta+1);
! 251: preferred = ifc->ifa_prefered;
! 252: valid = ifc->ifa_valid;
! 253: }
! 254: rta = RTA_NEXT(rta, len1);
! 255: }
! 256:
! 257: if (ifa->ifa_flags & IFA_F_TENTATIVE)
! 258: flags |= IFACE_TENTATIVE;
! 259:
! 260: if (ifa->ifa_flags & IFA_F_DEPRECATED)
! 261: flags |= IFACE_DEPRECATED;
! 262:
! 263: if (addrp && callback_ok)
! 264: if (!((*callback)(addrp, (int)(ifa->ifa_prefixlen), (int)(ifa->ifa_scope),
! 265: (int)(ifa->ifa_index), flags,
! 266: (int) preferred, (int)valid, parm)))
! 267: callback_ok = 0;
! 268: }
! 269: #endif
! 270: }
! 271: }
! 272: else if (h->nlmsg_type == RTM_NEWNEIGH && family == AF_UNSPEC)
! 273: {
! 274: struct ndmsg *neigh = NLMSG_DATA(h);
! 275: struct rtattr *rta = NDA_RTA(neigh);
! 276: unsigned int len1 = h->nlmsg_len - NLMSG_LENGTH(sizeof(*neigh));
! 277: size_t maclen = 0;
! 278: char *inaddr = NULL, *mac = NULL;
! 279:
! 280: while (RTA_OK(rta, len1))
! 281: {
! 282: if (rta->rta_type == NDA_DST)
! 283: inaddr = (char *)(rta+1);
! 284: else if (rta->rta_type == NDA_LLADDR)
! 285: {
! 286: maclen = rta->rta_len - sizeof(struct rtattr);
! 287: mac = (char *)(rta+1);
! 288: }
! 289:
! 290: rta = RTA_NEXT(rta, len1);
! 291: }
! 292:
! 293: if (inaddr && mac && callback_ok)
! 294: if (!((*callback)(neigh->ndm_family, inaddr, mac, maclen, parm)))
! 295: callback_ok = 0;
! 296: }
! 297: #ifdef HAVE_DHCP6
! 298: else if (h->nlmsg_type == RTM_NEWLINK && family == AF_LOCAL)
! 299: {
! 300: struct ifinfomsg *link = NLMSG_DATA(h);
! 301: struct rtattr *rta = IFLA_RTA(link);
! 302: unsigned int len1 = h->nlmsg_len - NLMSG_LENGTH(sizeof(*link));
! 303: char *mac = NULL;
! 304: size_t maclen = 0;
! 305:
! 306: while (RTA_OK(rta, len1))
! 307: {
! 308: if (rta->rta_type == IFLA_ADDRESS)
! 309: {
! 310: maclen = rta->rta_len - sizeof(struct rtattr);
! 311: mac = (char *)(rta+1);
! 312: }
! 313:
! 314: rta = RTA_NEXT(rta, len1);
! 315: }
! 316:
! 317: if (mac && callback_ok && !((link->ifi_flags & (IFF_LOOPBACK | IFF_POINTOPOINT))) &&
! 318: !((*callback)((int)link->ifi_index, (unsigned int)link->ifi_type, mac, maclen, parm)))
! 319: callback_ok = 0;
! 320: }
! 321: #endif
! 322: }
! 323: }
! 324:
! 325: void netlink_multicast(time_t now)
! 326: {
! 327: ssize_t len;
! 328: struct nlmsghdr *h;
! 329: int flags, newaddr = 0;
! 330:
! 331: /* don't risk blocking reading netlink messages here. */
! 332: if ((flags = fcntl(daemon->netlinkfd, F_GETFL)) == -1 ||
! 333: fcntl(daemon->netlinkfd, F_SETFL, flags | O_NONBLOCK) == -1)
! 334: return;
! 335:
! 336: if ((len = netlink_recv()) != -1)
! 337: for (h = (struct nlmsghdr *)iov.iov_base; NLMSG_OK(h, (size_t)len); h = NLMSG_NEXT(h, len))
! 338: if (nl_async(h))
! 339: newaddr = 1;
! 340:
! 341: /* restore non-blocking status */
! 342: fcntl(daemon->netlinkfd, F_SETFL, flags);
! 343:
! 344: if (newaddr)
! 345: nl_newaddress(now);
! 346: }
! 347:
! 348: static int nl_async(struct nlmsghdr *h)
! 349: {
! 350: if (h->nlmsg_type == NLMSG_ERROR)
! 351: {
! 352: struct nlmsgerr *err = NLMSG_DATA(h);
! 353: if (err->error != 0)
! 354: my_syslog(LOG_ERR, _("netlink returns error: %s"), strerror(-(err->error)));
! 355: return 0;
! 356: }
! 357: else if (h->nlmsg_pid == 0 && h->nlmsg_type == RTM_NEWROUTE)
! 358: {
! 359: /* We arrange to receive netlink multicast messages whenever the network route is added.
! 360: If this happens and we still have a DNS packet in the buffer, we re-send it.
! 361: This helps on DoD links, where frequently the packet which triggers dialling is
! 362: a DNS query, which then gets lost. By re-sending, we can avoid the lookup
! 363: failing. */
! 364: struct rtmsg *rtm = NLMSG_DATA(h);
! 365:
! 366: if (rtm->rtm_type == RTN_UNICAST && rtm->rtm_scope == RT_SCOPE_LINK)
! 367: {
! 368: /* Force re-reading resolv file right now, for luck. */
! 369: daemon->last_resolv = 0;
! 370:
! 371: if (daemon->srv_save)
! 372: {
! 373: int fd;
! 374:
! 375: if (daemon->srv_save->sfd)
! 376: fd = daemon->srv_save->sfd->fd;
! 377: else if (daemon->rfd_save && daemon->rfd_save->refcount != 0)
! 378: fd = daemon->rfd_save->fd;
! 379: else
! 380: return 0;
! 381:
! 382: while(sendto(fd, daemon->packet, daemon->packet_len, 0,
! 383: &daemon->srv_save->addr.sa, sa_len(&daemon->srv_save->addr)) == -1 && retry_send());
! 384: }
! 385: }
! 386: return 0;
! 387: }
! 388: else if (h->nlmsg_type == RTM_NEWADDR || h->nlmsg_type == RTM_DELADDR)
! 389: return 1; /* clever bind mode - rescan */
! 390:
! 391: return 0;
! 392: }
! 393:
! 394: static void nl_newaddress(time_t now)
! 395: {
! 396: if (option_bool(OPT_CLEVERBIND) || daemon->doing_dhcp6 || daemon->doing_ra)
! 397: enumerate_interfaces();
! 398:
! 399: if (option_bool(OPT_CLEVERBIND))
! 400: create_bound_listeners(0);
! 401:
! 402: #ifdef HAVE_DHCP6
! 403: if (daemon->doing_dhcp6 || daemon->doing_ra)
! 404: {
! 405: join_multicast(0);
! 406: dhcp_construct_contexts(now);
! 407: }
! 408:
! 409: if (daemon->doing_dhcp6)
! 410: lease_find_interfaces(now);
! 411: #endif
! 412: }
! 413:
! 414:
! 415: #endif
! 416:
! 417:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>