resolved-manager.c revision ec2c5e4398f9d65e5dfe61530f2556224733d1e6
6c03089c32c251d823173bda4d809a9e643219f0Lennart Poettering/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
6c03089c32c251d823173bda4d809a9e643219f0Lennart Poettering
6c03089c32c251d823173bda4d809a9e643219f0Lennart Poettering/***
6c03089c32c251d823173bda4d809a9e643219f0Lennart Poettering This file is part of systemd.
6c03089c32c251d823173bda4d809a9e643219f0Lennart Poettering
6c03089c32c251d823173bda4d809a9e643219f0Lennart Poettering Copyright 2014 Tom Gundersen <teg@jklm.no>
6c03089c32c251d823173bda4d809a9e643219f0Lennart Poettering
6c03089c32c251d823173bda4d809a9e643219f0Lennart Poettering systemd is free software; you can redistribute it and/or modify it
6c03089c32c251d823173bda4d809a9e643219f0Lennart Poettering under the terms of the GNU Lesser General Public License as published by
6c03089c32c251d823173bda4d809a9e643219f0Lennart Poettering the Free Software Foundation; either version 2.1 of the License, or
6c03089c32c251d823173bda4d809a9e643219f0Lennart Poettering (at your option) any later version.
6c03089c32c251d823173bda4d809a9e643219f0Lennart Poettering
6c03089c32c251d823173bda4d809a9e643219f0Lennart Poettering systemd is distributed in the hope that it will be useful, but
6c03089c32c251d823173bda4d809a9e643219f0Lennart Poettering WITHOUT ANY WARRANTY; without even the implied warranty of
6c03089c32c251d823173bda4d809a9e643219f0Lennart Poettering MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
6c03089c32c251d823173bda4d809a9e643219f0Lennart Poettering Lesser General Public License for more details.
6c03089c32c251d823173bda4d809a9e643219f0Lennart Poettering
6c03089c32c251d823173bda4d809a9e643219f0Lennart Poettering You should have received a copy of the GNU Lesser General Public License
6c03089c32c251d823173bda4d809a9e643219f0Lennart Poettering along with systemd; If not, see <http://www.gnu.org/licenses/>.
6c03089c32c251d823173bda4d809a9e643219f0Lennart Poettering ***/
6c03089c32c251d823173bda4d809a9e643219f0Lennart Poettering
96cde13ace6406582688028f3df5668a172ba628Zbigniew Jędrzejewski-Szmek#include <arpa/inet.h>
96cde13ace6406582688028f3df5668a172ba628Zbigniew Jędrzejewski-Szmek#include <resolv.h>
96cde13ace6406582688028f3df5668a172ba628Zbigniew Jędrzejewski-Szmek#include <net/if.h>
96cde13ace6406582688028f3df5668a172ba628Zbigniew Jędrzejewski-Szmek#include <sys/ioctl.h>
143bfdaf0b890fa7acadf02d1eafacaef1b696bdHolger Hans Peter Freyther#include <sys/poll.h>
96cde13ace6406582688028f3df5668a172ba628Zbigniew Jędrzejewski-Szmek#include <netinet/in.h>
7027ff61a34a12487712b382a061c654acc3a679Lennart Poettering
6c03089c32c251d823173bda4d809a9e643219f0Lennart Poettering#include "rtnl-util.h"
96cde13ace6406582688028f3df5668a172ba628Zbigniew Jędrzejewski-Szmek#include "event-util.h"
7027ff61a34a12487712b382a061c654acc3a679Lennart Poettering#include "network-util.h"
6c03089c32c251d823173bda4d809a9e643219f0Lennart Poettering#include "network-internal.h"
6c03089c32c251d823173bda4d809a9e643219f0Lennart Poettering#include "conf-parser.h"
96cde13ace6406582688028f3df5668a172ba628Zbigniew Jędrzejewski-Szmek#include "socket-util.h"
7027ff61a34a12487712b382a061c654acc3a679Lennart Poettering#include "af-list.h"
d7bd3de0654669e65b9642c248c5fa6d1d9a9f61Lennart Poettering#include "resolved.h"
d7bd3de0654669e65b9642c248c5fa6d1d9a9f61Lennart Poettering
d7bd3de0654669e65b9642c248c5fa6d1d9a9f61Lennart Poettering#define SEND_TIMEOUT_USEC (200 * USEC_PER_MSEC)
7027ff61a34a12487712b382a061c654acc3a679Lennart Poettering
7027ff61a34a12487712b382a061c654acc3a679Lennart Poetteringstatic int manager_process_link(sd_rtnl *rtnl, sd_rtnl_message *mm, void *userdata) {
7027ff61a34a12487712b382a061c654acc3a679Lennart Poettering Manager *m = userdata;
7027ff61a34a12487712b382a061c654acc3a679Lennart Poettering uint16_t type;
d7bd3de0654669e65b9642c248c5fa6d1d9a9f61Lennart Poettering Link *l;
d7bd3de0654669e65b9642c248c5fa6d1d9a9f61Lennart Poettering int ifindex, r;
6c03089c32c251d823173bda4d809a9e643219f0Lennart Poettering
6c03089c32c251d823173bda4d809a9e643219f0Lennart Poettering assert(rtnl);
6c03089c32c251d823173bda4d809a9e643219f0Lennart Poettering assert(m);
6c03089c32c251d823173bda4d809a9e643219f0Lennart Poettering assert(mm);
6c03089c32c251d823173bda4d809a9e643219f0Lennart Poettering
6c03089c32c251d823173bda4d809a9e643219f0Lennart Poettering r = sd_rtnl_message_get_type(mm, &type);
6c03089c32c251d823173bda4d809a9e643219f0Lennart Poettering if (r < 0)
6c03089c32c251d823173bda4d809a9e643219f0Lennart Poettering goto fail;
6c03089c32c251d823173bda4d809a9e643219f0Lennart Poettering
9444b1f20e311f073864d81e913bd4f32fe95cfdLennart Poettering r = sd_rtnl_message_link_get_ifindex(mm, &ifindex);
9444b1f20e311f073864d81e913bd4f32fe95cfdLennart Poettering if (r < 0)
d7bd3de0654669e65b9642c248c5fa6d1d9a9f61Lennart Poettering goto fail;
d7bd3de0654669e65b9642c248c5fa6d1d9a9f61Lennart Poettering
d7bd3de0654669e65b9642c248c5fa6d1d9a9f61Lennart Poettering l = hashmap_get(m->links, INT_TO_PTR(ifindex));
9444b1f20e311f073864d81e913bd4f32fe95cfdLennart Poettering
9444b1f20e311f073864d81e913bd4f32fe95cfdLennart Poettering switch (type) {
d7bd3de0654669e65b9642c248c5fa6d1d9a9f61Lennart Poettering
9444b1f20e311f073864d81e913bd4f32fe95cfdLennart Poettering case RTM_NEWLINK:{
d7bd3de0654669e65b9642c248c5fa6d1d9a9f61Lennart Poettering bool is_new = !l;
9444b1f20e311f073864d81e913bd4f32fe95cfdLennart Poettering
9444b1f20e311f073864d81e913bd4f32fe95cfdLennart Poettering if (!l) {
6c03089c32c251d823173bda4d809a9e643219f0Lennart Poettering r = link_new(m, &l, ifindex);
6c03089c32c251d823173bda4d809a9e643219f0Lennart Poettering if (r < 0)
6c03089c32c251d823173bda4d809a9e643219f0Lennart Poettering goto fail;
6c03089c32c251d823173bda4d809a9e643219f0Lennart Poettering }
6c03089c32c251d823173bda4d809a9e643219f0Lennart Poettering
6c03089c32c251d823173bda4d809a9e643219f0Lennart Poettering r = link_update_rtnl(l, mm);
6c03089c32c251d823173bda4d809a9e643219f0Lennart Poettering if (r < 0)
9444b1f20e311f073864d81e913bd4f32fe95cfdLennart Poettering goto fail;
374ec6abf31ada6ca554cc8ea99b282373fac010Lennart Poettering
374ec6abf31ada6ca554cc8ea99b282373fac010Lennart Poettering if (is_new)
374ec6abf31ada6ca554cc8ea99b282373fac010Lennart Poettering log_debug("Found new link %i/%s", ifindex, l->name);
374ec6abf31ada6ca554cc8ea99b282373fac010Lennart Poettering
374ec6abf31ada6ca554cc8ea99b282373fac010Lennart Poettering break;
d7bd3de0654669e65b9642c248c5fa6d1d9a9f61Lennart Poettering }
d7bd3de0654669e65b9642c248c5fa6d1d9a9f61Lennart Poettering
d7bd3de0654669e65b9642c248c5fa6d1d9a9f61Lennart Poettering case RTM_DELLINK:
9444b1f20e311f073864d81e913bd4f32fe95cfdLennart Poettering if (l) {
d7bd3de0654669e65b9642c248c5fa6d1d9a9f61Lennart Poettering log_debug("Removing link %i/%s", l->ifindex, l->name);
7027ff61a34a12487712b382a061c654acc3a679Lennart Poettering link_free(l);
6c03089c32c251d823173bda4d809a9e643219f0Lennart Poettering }
9444b1f20e311f073864d81e913bd4f32fe95cfdLennart Poettering
9444b1f20e311f073864d81e913bd4f32fe95cfdLennart Poettering break;
9444b1f20e311f073864d81e913bd4f32fe95cfdLennart Poettering }
9444b1f20e311f073864d81e913bd4f32fe95cfdLennart Poettering
9444b1f20e311f073864d81e913bd4f32fe95cfdLennart Poettering return 0;
96cde13ace6406582688028f3df5668a172ba628Zbigniew Jędrzejewski-Szmek
96cde13ace6406582688028f3df5668a172ba628Zbigniew Jędrzejewski-Szmekfail:
9444b1f20e311f073864d81e913bd4f32fe95cfdLennart Poettering log_warning("Failed to process RTNL link message: %s", strerror(-r));
374ec6abf31ada6ca554cc8ea99b282373fac010Lennart Poettering return 0;
374ec6abf31ada6ca554cc8ea99b282373fac010Lennart Poettering}
9444b1f20e311f073864d81e913bd4f32fe95cfdLennart Poettering
9444b1f20e311f073864d81e913bd4f32fe95cfdLennart Poetteringstatic int manager_process_address(sd_rtnl *rtnl, sd_rtnl_message *mm, void *userdata) {
7027ff61a34a12487712b382a061c654acc3a679Lennart Poettering Manager *m = userdata;
9444b1f20e311f073864d81e913bd4f32fe95cfdLennart Poettering union in_addr_union address;
9444b1f20e311f073864d81e913bd4f32fe95cfdLennart Poettering uint16_t type;
7027ff61a34a12487712b382a061c654acc3a679Lennart Poettering int r, ifindex, family;
9444b1f20e311f073864d81e913bd4f32fe95cfdLennart Poettering LinkAddress *a;
9444b1f20e311f073864d81e913bd4f32fe95cfdLennart Poettering Link *l;
9444b1f20e311f073864d81e913bd4f32fe95cfdLennart Poettering
9444b1f20e311f073864d81e913bd4f32fe95cfdLennart Poettering assert(rtnl);
9444b1f20e311f073864d81e913bd4f32fe95cfdLennart Poettering assert(mm);
374ec6abf31ada6ca554cc8ea99b282373fac010Lennart Poettering assert(m);
374ec6abf31ada6ca554cc8ea99b282373fac010Lennart Poettering
9444b1f20e311f073864d81e913bd4f32fe95cfdLennart Poettering r = sd_rtnl_message_get_type(mm, &type);
9444b1f20e311f073864d81e913bd4f32fe95cfdLennart Poettering if (r < 0)
7027ff61a34a12487712b382a061c654acc3a679Lennart Poettering goto fail;
9444b1f20e311f073864d81e913bd4f32fe95cfdLennart Poettering
9444b1f20e311f073864d81e913bd4f32fe95cfdLennart Poettering r = sd_rtnl_message_addr_get_ifindex(mm, &ifindex);
7027ff61a34a12487712b382a061c654acc3a679Lennart Poettering if (r < 0)
9444b1f20e311f073864d81e913bd4f32fe95cfdLennart Poettering goto fail;
9444b1f20e311f073864d81e913bd4f32fe95cfdLennart Poettering
9444b1f20e311f073864d81e913bd4f32fe95cfdLennart Poettering l = hashmap_get(m->links, INT_TO_PTR(ifindex));
9444b1f20e311f073864d81e913bd4f32fe95cfdLennart Poettering if (!l)
9444b1f20e311f073864d81e913bd4f32fe95cfdLennart Poettering return 0;
374ec6abf31ada6ca554cc8ea99b282373fac010Lennart Poettering
374ec6abf31ada6ca554cc8ea99b282373fac010Lennart Poettering r = sd_rtnl_message_addr_get_family(mm, &family);
374ec6abf31ada6ca554cc8ea99b282373fac010Lennart Poettering if (r < 0)
374ec6abf31ada6ca554cc8ea99b282373fac010Lennart Poettering goto fail;
9444b1f20e311f073864d81e913bd4f32fe95cfdLennart Poettering
9444b1f20e311f073864d81e913bd4f32fe95cfdLennart Poettering switch (family) {
9444b1f20e311f073864d81e913bd4f32fe95cfdLennart Poettering
9444b1f20e311f073864d81e913bd4f32fe95cfdLennart Poettering case AF_INET:
9444b1f20e311f073864d81e913bd4f32fe95cfdLennart Poettering r = sd_rtnl_message_read_in_addr(mm, IFA_LOCAL, &address.in);
9444b1f20e311f073864d81e913bd4f32fe95cfdLennart Poettering if (r < 0) {
9444b1f20e311f073864d81e913bd4f32fe95cfdLennart Poettering r = sd_rtnl_message_read_in_addr(mm, IFA_ADDRESS, &address.in);
9444b1f20e311f073864d81e913bd4f32fe95cfdLennart Poettering if (r < 0)
7027ff61a34a12487712b382a061c654acc3a679Lennart Poettering goto fail;
7027ff61a34a12487712b382a061c654acc3a679Lennart Poettering }
aff38e74bd776471f15ba54b305a24b0251eb865Lennart Poettering
aff38e74bd776471f15ba54b305a24b0251eb865Lennart Poettering break;
aff38e74bd776471f15ba54b305a24b0251eb865Lennart Poettering
aff38e74bd776471f15ba54b305a24b0251eb865Lennart Poettering case AF_INET6:
aff38e74bd776471f15ba54b305a24b0251eb865Lennart Poettering r = sd_rtnl_message_read_in6_addr(mm, IFA_LOCAL, &address.in6);
aff38e74bd776471f15ba54b305a24b0251eb865Lennart Poettering if (r < 0) {
aff38e74bd776471f15ba54b305a24b0251eb865Lennart Poettering r = sd_rtnl_message_read_in6_addr(mm, IFA_ADDRESS, &address.in6);
aff38e74bd776471f15ba54b305a24b0251eb865Lennart Poettering if (r < 0)
aff38e74bd776471f15ba54b305a24b0251eb865Lennart Poettering goto fail;
1021b21bc6f8dd522b46116e8598b17f9f93f1b7Lennart Poettering }
aff38e74bd776471f15ba54b305a24b0251eb865Lennart Poettering
ae018d9bc900d6355dea4af05119b49c67945184Lennart Poettering break;
aff38e74bd776471f15ba54b305a24b0251eb865Lennart Poettering
aff38e74bd776471f15ba54b305a24b0251eb865Lennart Poettering default:
aff38e74bd776471f15ba54b305a24b0251eb865Lennart Poettering return 0;
aff38e74bd776471f15ba54b305a24b0251eb865Lennart Poettering }
aff38e74bd776471f15ba54b305a24b0251eb865Lennart Poettering
aff38e74bd776471f15ba54b305a24b0251eb865Lennart Poettering a = link_find_address(l, family, &address);
aff38e74bd776471f15ba54b305a24b0251eb865Lennart Poettering
aff38e74bd776471f15ba54b305a24b0251eb865Lennart Poettering switch (type) {
aff38e74bd776471f15ba54b305a24b0251eb865Lennart Poettering
ae018d9bc900d6355dea4af05119b49c67945184Lennart Poettering case RTM_NEWADDR:
ae018d9bc900d6355dea4af05119b49c67945184Lennart Poettering
ae018d9bc900d6355dea4af05119b49c67945184Lennart Poettering if (!a) {
aff38e74bd776471f15ba54b305a24b0251eb865Lennart Poettering r = link_address_new(l, &a, family, &address);
aff38e74bd776471f15ba54b305a24b0251eb865Lennart Poettering if (r < 0)
ae018d9bc900d6355dea4af05119b49c67945184Lennart Poettering return r;
aff38e74bd776471f15ba54b305a24b0251eb865Lennart Poettering }
aff38e74bd776471f15ba54b305a24b0251eb865Lennart Poettering
aff38e74bd776471f15ba54b305a24b0251eb865Lennart Poettering r = link_address_update_rtnl(a, mm);
aff38e74bd776471f15ba54b305a24b0251eb865Lennart Poettering if (r < 0)
1021b21bc6f8dd522b46116e8598b17f9f93f1b7Lennart Poettering return r;
aff38e74bd776471f15ba54b305a24b0251eb865Lennart Poettering
1021b21bc6f8dd522b46116e8598b17f9f93f1b7Lennart Poettering break;
aff38e74bd776471f15ba54b305a24b0251eb865Lennart Poettering
aff38e74bd776471f15ba54b305a24b0251eb865Lennart Poettering case RTM_DELADDR:
aff38e74bd776471f15ba54b305a24b0251eb865Lennart Poettering if (a)
aff38e74bd776471f15ba54b305a24b0251eb865Lennart Poettering link_address_free(a);
ae018d9bc900d6355dea4af05119b49c67945184Lennart Poettering break;
aff38e74bd776471f15ba54b305a24b0251eb865Lennart Poettering }
aff38e74bd776471f15ba54b305a24b0251eb865Lennart Poettering
aff38e74bd776471f15ba54b305a24b0251eb865Lennart Poettering return 0;
1021b21bc6f8dd522b46116e8598b17f9f93f1b7Lennart Poettering
1021b21bc6f8dd522b46116e8598b17f9f93f1b7Lennart Poetteringfail:
aff38e74bd776471f15ba54b305a24b0251eb865Lennart Poettering log_warning("Failed to process RTNL address message: %s", strerror(-r));
aff38e74bd776471f15ba54b305a24b0251eb865Lennart Poettering return 0;
aff38e74bd776471f15ba54b305a24b0251eb865Lennart Poettering}
ae018d9bc900d6355dea4af05119b49c67945184Lennart Poettering
ae018d9bc900d6355dea4af05119b49c67945184Lennart Poettering
ae018d9bc900d6355dea4af05119b49c67945184Lennart Poetteringstatic int manager_rtnl_listen(Manager *m) {
ae018d9bc900d6355dea4af05119b49c67945184Lennart Poettering _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL;
ae018d9bc900d6355dea4af05119b49c67945184Lennart Poettering sd_rtnl_message *i;
ae018d9bc900d6355dea4af05119b49c67945184Lennart Poettering int r;
ae018d9bc900d6355dea4af05119b49c67945184Lennart Poettering
ae018d9bc900d6355dea4af05119b49c67945184Lennart Poettering assert(m);
ae018d9bc900d6355dea4af05119b49c67945184Lennart Poettering
ae018d9bc900d6355dea4af05119b49c67945184Lennart Poettering /* First, subscibe to interfaces coming and going */
ae018d9bc900d6355dea4af05119b49c67945184Lennart Poettering r = sd_rtnl_open(&m->rtnl, 3, RTNLGRP_LINK, RTNLGRP_IPV4_IFADDR, RTNLGRP_IPV6_IFADDR);
ae018d9bc900d6355dea4af05119b49c67945184Lennart Poettering if (r < 0)
a0ab566574303be1ca12cdb334f284cfd407caa5Lennart Poettering return r;
ae018d9bc900d6355dea4af05119b49c67945184Lennart Poettering
ae018d9bc900d6355dea4af05119b49c67945184Lennart Poettering r = sd_rtnl_attach_event(m->rtnl, m->event, 0);
ae018d9bc900d6355dea4af05119b49c67945184Lennart Poettering if (r < 0)
ae018d9bc900d6355dea4af05119b49c67945184Lennart Poettering return r;
ae018d9bc900d6355dea4af05119b49c67945184Lennart Poettering
a0ab566574303be1ca12cdb334f284cfd407caa5Lennart Poettering r = sd_rtnl_add_match(m->rtnl, RTM_NEWLINK, manager_process_link, m);
a0ab566574303be1ca12cdb334f284cfd407caa5Lennart Poettering if (r < 0)
a0ab566574303be1ca12cdb334f284cfd407caa5Lennart Poettering return r;
ae018d9bc900d6355dea4af05119b49c67945184Lennart Poettering
ae018d9bc900d6355dea4af05119b49c67945184Lennart Poettering r = sd_rtnl_add_match(m->rtnl, RTM_DELLINK, manager_process_link, m);
78edb35ab4f4227485cb9ec816b43c37e0d5e62aLennart Poettering if (r < 0)
78edb35ab4f4227485cb9ec816b43c37e0d5e62aLennart Poettering return r;
78edb35ab4f4227485cb9ec816b43c37e0d5e62aLennart Poettering
78edb35ab4f4227485cb9ec816b43c37e0d5e62aLennart Poettering r = sd_rtnl_add_match(m->rtnl, RTM_NEWADDR, manager_process_address, m);
78edb35ab4f4227485cb9ec816b43c37e0d5e62aLennart Poettering if (r < 0)
78edb35ab4f4227485cb9ec816b43c37e0d5e62aLennart Poettering return r;
78edb35ab4f4227485cb9ec816b43c37e0d5e62aLennart Poettering
78edb35ab4f4227485cb9ec816b43c37e0d5e62aLennart Poettering r = sd_rtnl_add_match(m->rtnl, RTM_DELADDR, manager_process_address, m);
78edb35ab4f4227485cb9ec816b43c37e0d5e62aLennart Poettering if (r < 0)
78edb35ab4f4227485cb9ec816b43c37e0d5e62aLennart Poettering return r;
78edb35ab4f4227485cb9ec816b43c37e0d5e62aLennart Poettering
78edb35ab4f4227485cb9ec816b43c37e0d5e62aLennart Poettering /* Then, enumerate all links */
78edb35ab4f4227485cb9ec816b43c37e0d5e62aLennart Poettering r = sd_rtnl_message_new_link(m->rtnl, &req, RTM_GETLINK, 0);
a016b9228f338cb9b380ce7e00826ef462767d98Lennart Poettering if (r < 0)
a016b9228f338cb9b380ce7e00826ef462767d98Lennart Poettering return r;
a016b9228f338cb9b380ce7e00826ef462767d98Lennart Poettering
a016b9228f338cb9b380ce7e00826ef462767d98Lennart Poettering r = sd_rtnl_message_request_dump(req, true);
a016b9228f338cb9b380ce7e00826ef462767d98Lennart Poettering if (r < 0)
a016b9228f338cb9b380ce7e00826ef462767d98Lennart Poettering return r;
a016b9228f338cb9b380ce7e00826ef462767d98Lennart Poettering
a016b9228f338cb9b380ce7e00826ef462767d98Lennart Poettering r = sd_rtnl_call(m->rtnl, req, 0, &reply);
a016b9228f338cb9b380ce7e00826ef462767d98Lennart Poettering if (r < 0)
a016b9228f338cb9b380ce7e00826ef462767d98Lennart Poettering return r;
a016b9228f338cb9b380ce7e00826ef462767d98Lennart Poettering
a016b9228f338cb9b380ce7e00826ef462767d98Lennart Poettering for (i = reply; i; i = sd_rtnl_message_next(i)) {
a016b9228f338cb9b380ce7e00826ef462767d98Lennart Poettering r = manager_process_link(m->rtnl, i, m);
a016b9228f338cb9b380ce7e00826ef462767d98Lennart Poettering if (r < 0)
a016b9228f338cb9b380ce7e00826ef462767d98Lennart Poettering return r;
a016b9228f338cb9b380ce7e00826ef462767d98Lennart Poettering }
a016b9228f338cb9b380ce7e00826ef462767d98Lennart Poettering
a016b9228f338cb9b380ce7e00826ef462767d98Lennart Poettering req = sd_rtnl_message_unref(req);
a016b9228f338cb9b380ce7e00826ef462767d98Lennart Poettering reply = sd_rtnl_message_unref(reply);
96cde13ace6406582688028f3df5668a172ba628Zbigniew Jędrzejewski-Szmek
7027ff61a34a12487712b382a061c654acc3a679Lennart Poettering /* Finally, enumerate all addresses, too */
6c03089c32c251d823173bda4d809a9e643219f0Lennart Poettering r = sd_rtnl_message_new_addr(m->rtnl, &req, RTM_GETADDR, 0, AF_UNSPEC);
7027ff61a34a12487712b382a061c654acc3a679Lennart Poettering if (r < 0)
9444b1f20e311f073864d81e913bd4f32fe95cfdLennart Poettering return r;
9444b1f20e311f073864d81e913bd4f32fe95cfdLennart Poettering
9444b1f20e311f073864d81e913bd4f32fe95cfdLennart Poettering r = sd_rtnl_message_request_dump(req, true);
143bfdaf0b890fa7acadf02d1eafacaef1b696bdHolger Hans Peter Freyther if (r < 0)
aff38e74bd776471f15ba54b305a24b0251eb865Lennart Poettering return r;
143bfdaf0b890fa7acadf02d1eafacaef1b696bdHolger Hans Peter Freyther
78edb35ab4f4227485cb9ec816b43c37e0d5e62aLennart Poettering r = sd_rtnl_call(m->rtnl, req, 0, &reply);
a016b9228f338cb9b380ce7e00826ef462767d98Lennart Poettering if (r < 0)
96cde13ace6406582688028f3df5668a172ba628Zbigniew Jędrzejewski-Szmek return r;
96cde13ace6406582688028f3df5668a172ba628Zbigniew Jędrzejewski-Szmek
96cde13ace6406582688028f3df5668a172ba628Zbigniew Jędrzejewski-Szmek for (i = reply; i; i = sd_rtnl_message_next(i)) {
r = manager_process_address(m->rtnl, i, m);
if (r < 0)
return r;
}
return r;
}
static int on_network_event(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
Manager *m = userdata;
Iterator i;
Link *l;
int r;
assert(m);
sd_network_monitor_flush(m->network_monitor);
HASHMAP_FOREACH(l, m->links, i) {
r = link_update_monitor(l);
if (r < 0)
log_warning("Failed to update monitor information for %i: %s", l->ifindex, strerror(-r));
}
r = manager_write_resolv_conf(m);
if (r < 0)
log_warning("Could not update resolv.conf: %s", strerror(-r));
return 0;
}
static int manager_network_monitor_listen(Manager *m) {
int r, fd, events;
assert(m);
r = sd_network_monitor_new(&m->network_monitor, NULL);
if (r < 0)
return r;
fd = sd_network_monitor_get_fd(m->network_monitor);
if (fd < 0)
return fd;
events = sd_network_monitor_get_events(m->network_monitor);
if (events < 0)
return events;
r = sd_event_add_io(m->event, &m->network_event_source, fd, events, &on_network_event, m);
if (r < 0)
return r;
return 0;
}
static int parse_dns_server_string(Manager *m, const char *string) {
const char *word, *state;
size_t length;
int r;
assert(m);
assert(string);
FOREACH_WORD_QUOTED(word, length, string, state) {
char buffer[length+1];
int family;
union in_addr_union addr;
memcpy(buffer, word, length);
buffer[length] = 0;
r = in_addr_from_string_auto(buffer, &family, &addr);
if (r < 0) {
log_warning("Ignoring invalid DNS address '%s'", buffer);
continue;
}
/* filter out duplicates */
if (manager_find_dns_server(m, family, &addr))
continue;
r = dns_server_new(m, NULL, NULL, family, &addr);
if (r < 0)
return r;
}
/* do not warn about state here, since probably systemd already did */
return 0;
}
int config_parse_dnsv(
const char *unit,
const char *filename,
unsigned line,
const char *section,
unsigned section_line,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
Manager *m = userdata;
int r;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(m);
/* Empty assignment means clear the list */
if (isempty(rvalue)) {
while (m->dns_servers)
dns_server_free(m->dns_servers);
return 0;
}
r = parse_dns_server_string(m, rvalue);
if (r < 0) {
log_error("Failed to parse DNS server string");
return r;
}
return 0;
}
int manager_parse_config_file(Manager *m) {
assert(m);
return config_parse(NULL, "/etc/systemd/resolved.conf", NULL,
"Resolve\0",
config_item_perf_lookup, resolved_gperf_lookup,
false, false, true, m);
}
int manager_new(Manager **ret) {
_cleanup_(manager_freep) Manager *m = NULL;
int r;
assert(ret);
m = new0(Manager, 1);
if (!m)
return -ENOMEM;
m->dns_ipv4_fd = m->dns_ipv6_fd = -1;
m->llmnr_ipv4_udp_fd = m->llmnr_ipv6_udp_fd = -1;
m->llmnr_ipv4_tcp_fd = m->llmnr_ipv6_tcp_fd = -1;
m->use_llmnr = true;
r = parse_dns_server_string(m, DNS_SERVERS);
if (r < 0)
return r;
m->hostname = gethostname_malloc();
if (!m->hostname)
return -ENOMEM;
r = sd_event_default(&m->event);
if (r < 0)
return r;
sd_event_add_signal(m->event, NULL, SIGTERM, NULL, NULL);
sd_event_add_signal(m->event, NULL, SIGINT, NULL, NULL);
sd_event_set_watchdog(m->event, true);
r = dns_scope_new(m, &m->unicast_scope, NULL, DNS_PROTOCOL_DNS, AF_UNSPEC);
if (r < 0)
return r;
r = manager_network_monitor_listen(m);
if (r < 0)
return r;
r = manager_rtnl_listen(m);
if (r < 0)
return r;
r = manager_connect_bus(m);
if (r < 0)
return r;
r = manager_llmnr_ipv4_udp_fd(m);
if (r < 0)
return r;
r = manager_llmnr_ipv6_udp_fd(m);
if (r < 0)
return r;
r = manager_llmnr_ipv4_tcp_fd(m);
if (r < 0)
return r;
r = manager_llmnr_ipv6_tcp_fd(m);
if (r < 0)
return r;
*ret = m;
m = NULL;
return 0;
}
Manager *manager_free(Manager *m) {
Link *l;
if (!m)
return NULL;
while (m->dns_queries)
dns_query_free(m->dns_queries);
hashmap_free(m->dns_transactions);
while ((l = hashmap_first(m->links)))
link_free(l);
hashmap_free(m->links);
dns_scope_free(m->unicast_scope);
while (m->dns_servers)
dns_server_free(m->dns_servers);
sd_event_source_unref(m->network_event_source);
sd_network_monitor_unref(m->network_monitor);
sd_event_source_unref(m->dns_ipv4_event_source);
sd_event_source_unref(m->dns_ipv6_event_source);
safe_close(m->dns_ipv4_fd);
safe_close(m->dns_ipv6_fd);
sd_event_source_unref(m->llmnr_ipv4_udp_event_source);
sd_event_source_unref(m->llmnr_ipv6_udp_event_source);
safe_close(m->llmnr_ipv4_udp_fd);
safe_close(m->llmnr_ipv6_udp_fd);
sd_event_source_unref(m->llmnr_ipv4_tcp_event_source);
sd_event_source_unref(m->llmnr_ipv6_tcp_event_source);
safe_close(m->llmnr_ipv4_tcp_fd);
safe_close(m->llmnr_ipv6_tcp_fd);
sd_event_source_unref(m->bus_retry_event_source);
sd_bus_unref(m->bus);
sd_event_unref(m->event);
dns_resource_key_unref(m->host_ipv4_key);
dns_resource_key_unref(m->host_ipv6_key);
free(m->hostname);
free(m);
return NULL;
}
static void write_resolve_conf_server(DnsServer *s, FILE *f, unsigned *count) {
_cleanup_free_ char *t = NULL;
int r;
assert(s);
assert(f);
assert(count);
r = in_addr_to_string(s->family, &s->address, &t);
if (r < 0) {
log_warning("Invalid DNS address. Ignoring.");
return;
}
if (*count == MAXNS)
fputs("# Too many DNS servers configured, the following entries may be ignored\n", f);
fprintf(f, "nameserver %s\n", t);
(*count) ++;
}
int manager_write_resolv_conf(Manager *m) {
const char *path = "/run/systemd/resolve/resolv.conf";
_cleanup_free_ char *temp_path = NULL;
_cleanup_fclose_ FILE *f = NULL;
unsigned count = 0;
DnsServer *s;
Iterator i;
Link *l;
int r;
assert(m);
r = fopen_temporary(path, &f, &temp_path);
if (r < 0)
return r;
fchmod(fileno(f), 0644);
fputs("# This file is managed by systemd-resolved(8). Do not edit.\n#\n"
"# Third party programs must not access this file directly, but\n"
"# only through the symlink at /etc/resolv.conf. To manage\n"
"# resolv.conf(5) in a different way, replace the symlink by a\n"
"# static file or a different symlink.\n\n", f);
HASHMAP_FOREACH(l, m->links, i)
LIST_FOREACH(servers, s, l->dns_servers)
write_resolve_conf_server(s, f, &count);
LIST_FOREACH(servers, s, m->dns_servers)
write_resolve_conf_server(s, f, &count);
r = fflush_and_check(f);
if (r < 0)
goto fail;
if (rename(temp_path, path) < 0) {
r = -errno;
goto fail;
}
return 0;
fail:
unlink(path);
unlink(temp_path);
return r;
}
int manager_recv(Manager *m, int fd, DnsProtocol protocol, DnsPacket **ret) {
_cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
union {
struct cmsghdr header; /* For alignment */
uint8_t buffer[CMSG_SPACE(MAX(sizeof(struct in_pktinfo), sizeof(struct in6_pktinfo)))
+ CMSG_SPACE(int) /* ttl/hoplimit */
+ EXTRA_CMSG_SPACE /* kernel appears to require extra buffer space */];
} control;
union sockaddr_union sa;
struct msghdr mh = {};
struct cmsghdr *cmsg;
struct iovec iov;
int ms = 0, r;
ssize_t l;
assert(m);
assert(fd >= 0);
assert(ret);
r = ioctl(fd, FIONREAD, &ms);
if (r < 0)
return -errno;
if (ms < 0)
return -EIO;
r = dns_packet_new(&p, protocol, ms);
if (r < 0)
return r;
iov.iov_base = DNS_PACKET_DATA(p);
iov.iov_len = p->allocated;
mh.msg_name = &sa.sa;
mh.msg_namelen = sizeof(sa);
mh.msg_iov = &iov;
mh.msg_iovlen = 1;
mh.msg_control = &control;
mh.msg_controllen = sizeof(control);
l = recvmsg(fd, &mh, 0);
if (l < 0) {
if (errno == EAGAIN || errno == EINTR)
return 0;
return -errno;
}
if (l <= 0)
return -EIO;
assert(!(mh.msg_flags & MSG_CTRUNC));
assert(!(mh.msg_flags & MSG_TRUNC));
p->size = (size_t) l;
p->family = sa.sa.sa_family;
p->ipproto = IPPROTO_UDP;
if (p->family == AF_INET) {
p->sender.in = sa.in.sin_addr;
p->sender_port = be16toh(sa.in.sin_port);
} else if (p->family == AF_INET6) {
p->sender.in6 = sa.in6.sin6_addr;
p->sender_port = be16toh(sa.in6.sin6_port);
p->ifindex = sa.in6.sin6_scope_id;
} else
return -EAFNOSUPPORT;
for (cmsg = CMSG_FIRSTHDR(&mh); cmsg; cmsg = CMSG_NXTHDR(&mh, cmsg)) {
if (cmsg->cmsg_level == IPPROTO_IPV6) {
assert(p->family == AF_INET6);
switch (cmsg->cmsg_type) {
case IPV6_PKTINFO: {
struct in6_pktinfo *i = (struct in6_pktinfo*) CMSG_DATA(cmsg);
if (p->ifindex <= 0)
p->ifindex = i->ipi6_ifindex;
p->destination.in6 = i->ipi6_addr;
break;
}
case IPV6_HOPLIMIT:
p->ttl = *(int *) CMSG_DATA(cmsg);
break;
}
} else if (cmsg->cmsg_level == IPPROTO_IP) {
assert(p->family == AF_INET);
switch (cmsg->cmsg_type) {
case IP_PKTINFO: {
struct in_pktinfo *i = (struct in_pktinfo*) CMSG_DATA(cmsg);
if (p->ifindex <= 0)
p->ifindex = i->ipi_ifindex;
p->destination.in = i->ipi_addr;
break;
}
case IP_TTL:
p->ttl = *(int *) CMSG_DATA(cmsg);
break;
}
}
}
/* The Linux kernel sets the interface index to the loopback
* device if the packet came from the local host since it
* avoids the routing table in such a case. Let's unset the
* interface index in such a case. */
if (p->ifindex > 0 && manager_ifindex_is_loopback(m, p->ifindex) != 0)
p->ifindex = 0;
/* If we don't know the interface index still, we look for the
* first local interface with a matching address. Yuck! */
if (p->ifindex <= 0)
p->ifindex = manager_find_ifindex(m, p->family, &p->destination);
*ret = p;
p = NULL;
return 1;
}
static int on_dns_packet(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
_cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
DnsTransaction *t = NULL;
Manager *m = userdata;
int r;
r = manager_recv(m, fd, DNS_PROTOCOL_DNS, &p);
if (r <= 0)
return r;
if (dns_packet_validate_reply(p) > 0) {
t = hashmap_get(m->dns_transactions, UINT_TO_PTR(DNS_PACKET_ID(p)));
if (!t)
return 0;
dns_transaction_process_reply(t, p);
} else
log_debug("Invalid DNS packet.");
return 0;
}
int manager_dns_ipv4_fd(Manager *m) {
const int one = 1;
int r;
assert(m);
if (m->dns_ipv4_fd >= 0)
return m->dns_ipv4_fd;
m->dns_ipv4_fd = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
if (m->dns_ipv4_fd < 0)
return -errno;
r = setsockopt(m->dns_ipv4_fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof(one));
if (r < 0) {
r = -errno;
goto fail;
}
r = sd_event_add_io(m->event, &m->dns_ipv4_event_source, m->dns_ipv4_fd, EPOLLIN, on_dns_packet, m);
if (r < 0)
goto fail;
return m->dns_ipv4_fd;
fail:
m->dns_ipv4_fd = safe_close(m->dns_ipv4_fd);
return r;
}
int manager_dns_ipv6_fd(Manager *m) {
const int one = 1;
int r;
assert(m);
if (m->dns_ipv6_fd >= 0)
return m->dns_ipv6_fd;
m->dns_ipv6_fd = socket(AF_INET6, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
if (m->dns_ipv6_fd < 0)
return -errno;
r = setsockopt(m->dns_ipv6_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &one, sizeof(one));
if (r < 0) {
r = -errno;
goto fail;
}
r = sd_event_add_io(m->event, &m->dns_ipv6_event_source, m->dns_ipv6_fd, EPOLLIN, on_dns_packet, m);
if (r < 0)
goto fail;
return m->dns_ipv6_fd;
fail:
m->dns_ipv6_fd = safe_close(m->dns_ipv6_fd);
return r;
}
static int sendmsg_loop(int fd, struct msghdr *mh, int flags) {
int r;
assert(fd >= 0);
assert(mh);
for (;;) {
if (sendmsg(fd, mh, flags) >= 0)
return 0;
if (errno == EINTR)
continue;
if (errno != EAGAIN)
return -errno;
r = fd_wait_for_event(fd, POLLOUT, SEND_TIMEOUT_USEC);
if (r < 0)
return r;
if (r == 0)
return -ETIMEDOUT;
}
}
static int manager_ipv4_send(Manager *m, int fd, int ifindex, const struct in_addr *addr, uint16_t port, DnsPacket *p) {
union sockaddr_union sa = {
.in.sin_family = AF_INET,
};
union {
struct cmsghdr header; /* For alignment */
uint8_t buffer[CMSG_SPACE(sizeof(struct in_pktinfo))];
} control;
struct msghdr mh = {};
struct iovec iov;
assert(m);
assert(fd >= 0);
assert(addr);
assert(port > 0);
assert(p);
iov.iov_base = DNS_PACKET_DATA(p);
iov.iov_len = p->size;
sa.in.sin_addr = *addr;
sa.in.sin_port = htobe16(port),
mh.msg_iov = &iov;
mh.msg_iovlen = 1;
mh.msg_name = &sa.sa;
mh.msg_namelen = sizeof(sa.in);
if (ifindex > 0) {
struct cmsghdr *cmsg;
struct in_pktinfo *pi;
zero(control);
mh.msg_control = &control;
mh.msg_controllen = CMSG_LEN(sizeof(struct in_pktinfo));
cmsg = CMSG_FIRSTHDR(&mh);
cmsg->cmsg_len = mh.msg_controllen;
cmsg->cmsg_level = IPPROTO_IP;
cmsg->cmsg_type = IP_PKTINFO;
pi = (struct in_pktinfo*) CMSG_DATA(cmsg);
pi->ipi_ifindex = ifindex;
}
return sendmsg_loop(fd, &mh, 0);
}
static int manager_ipv6_send(Manager *m, int fd, int ifindex, const struct in6_addr *addr, uint16_t port, DnsPacket *p) {
union sockaddr_union sa = {
.in6.sin6_family = AF_INET6,
};
union {
struct cmsghdr header; /* For alignment */
uint8_t buffer[CMSG_SPACE(sizeof(struct in6_pktinfo))];
} control;
struct msghdr mh = {};
struct iovec iov;
assert(m);
assert(fd >= 0);
assert(addr);
assert(port > 0);
assert(p);
iov.iov_base = DNS_PACKET_DATA(p);
iov.iov_len = p->size;
sa.in6.sin6_addr = *addr;
sa.in6.sin6_port = htobe16(port),
sa.in6.sin6_scope_id = ifindex;
mh.msg_iov = &iov;
mh.msg_iovlen = 1;
mh.msg_name = &sa.sa;
mh.msg_namelen = sizeof(sa.in6);
if (ifindex > 0) {
struct cmsghdr *cmsg;
struct in6_pktinfo *pi;
zero(control);
mh.msg_control = &control;
mh.msg_controllen = CMSG_LEN(sizeof(struct in6_pktinfo));
cmsg = CMSG_FIRSTHDR(&mh);
cmsg->cmsg_len = mh.msg_controllen;
cmsg->cmsg_level = IPPROTO_IPV6;
cmsg->cmsg_type = IPV6_PKTINFO;
pi = (struct in6_pktinfo*) CMSG_DATA(cmsg);
pi->ipi6_ifindex = ifindex;
}
return sendmsg_loop(fd, &mh, 0);
}
int manager_send(Manager *m, int fd, int ifindex, int family, const union in_addr_union *addr, uint16_t port, DnsPacket *p) {
assert(m);
assert(fd >= 0);
assert(addr);
assert(port > 0);
assert(p);
log_debug("Sending %s packet with id %u on interface %i/%s", DNS_PACKET_QR(p) ? "response" : "query", DNS_PACKET_ID(p), ifindex, af_to_name(family));
if (family == AF_INET)
return manager_ipv4_send(m, fd, ifindex, &addr->in, port, p);
else if (family == AF_INET6)
return manager_ipv6_send(m, fd, ifindex, &addr->in6, port, p);
return -EAFNOSUPPORT;
}
DnsServer* manager_find_dns_server(Manager *m, int family, const union in_addr_union *in_addr) {
DnsServer *s;
assert(m);
assert(in_addr);
LIST_FOREACH(servers, s, m->dns_servers) {
if (s->family == family &&
in_addr_equal(family, &s->address, in_addr))
return s;
}
return NULL;
}
DnsServer *manager_get_dns_server(Manager *m) {
assert(m);
if (!m->current_dns_server)
m->current_dns_server = m->dns_servers;
return m->current_dns_server;
}
void manager_next_dns_server(Manager *m) {
assert(m);
if (!m->current_dns_server) {
m->current_dns_server = m->dns_servers;
return;
}
if (!m->current_dns_server)
return;
if (m->current_dns_server->servers_next) {
m->current_dns_server = m->current_dns_server->servers_next;
return;
}
m->current_dns_server = m->dns_servers;
}
uint32_t manager_find_mtu(Manager *m) {
uint32_t mtu = 0;
Link *l;
Iterator i;
/* If we don't know on which link a DNS packet would be
* delivered, let's find the largest MTU that works on all
* interfaces we know of */
HASHMAP_FOREACH(l, m->links, i) {
if (l->mtu <= 0)
continue;
if (mtu <= 0 || l->mtu < mtu)
mtu = l->mtu;
}
return mtu;
}
static int on_llmnr_packet(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
_cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
DnsTransaction *t = NULL;
Manager *m = userdata;
int r;
r = manager_recv(m, fd, DNS_PROTOCOL_LLMNR, &p);
if (r <= 0)
return r;
if (dns_packet_validate_reply(p) > 0) {
log_debug("Got reply packet for id %u", DNS_PACKET_ID(p));
t = hashmap_get(m->dns_transactions, UINT_TO_PTR(DNS_PACKET_ID(p)));
if (!t)
return 0;
dns_transaction_process_reply(t, p);
} else if (dns_packet_validate_query(p) > 0) {
Link *l;
l = hashmap_get(m->links, INT_TO_PTR(p->ifindex));
if (l) {
DnsScope *scope = NULL;
if (p->family == AF_INET)
scope = l->llmnr_ipv4_scope;
else if (p->family == AF_INET6)
scope = l->llmnr_ipv6_scope;
if (scope)
dns_scope_process_query(scope, NULL, p);
}
} else
log_debug("Invalid LLMNR packet.");
return 0;
}
int manager_llmnr_ipv4_udp_fd(Manager *m) {
union sockaddr_union sa = {
.in.sin_family = AF_INET,
.in.sin_port = htobe16(5355),
};
static const int one = 1, pmtu = IP_PMTUDISC_DONT, ttl = 255;
int r;
assert(m);
if (m->llmnr_ipv4_udp_fd >= 0)
return m->llmnr_ipv4_udp_fd;
m->llmnr_ipv4_udp_fd = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
if (m->llmnr_ipv4_udp_fd < 0)
return -errno;
/* RFC 4795, section 2.5 recommends setting the TTL of UDP packets to 255. */
r = setsockopt(m->llmnr_ipv4_udp_fd, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl));
if (r < 0) {
r = -errno;
goto fail;
}
r = setsockopt(m->llmnr_ipv4_udp_fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl));
if (r < 0) {
r = -errno;
goto fail;
}
r = setsockopt(m->llmnr_ipv4_udp_fd, IPPROTO_IP, IP_MULTICAST_LOOP, &one, sizeof(one));
if (r < 0) {
r = -errno;
goto fail;
}
r = setsockopt(m->llmnr_ipv4_udp_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
if (r < 0) {
r = -errno;
goto fail;
}
r = setsockopt(m->llmnr_ipv4_udp_fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof(one));
if (r < 0) {
r = -errno;
goto fail;
}
r = setsockopt(m->llmnr_ipv4_udp_fd, IPPROTO_IP, IP_RECVTTL, &one, sizeof(one));
if (r < 0) {
r = -errno;
goto fail;
}
/* Disable Don't-Fragment bit in the IP header */
r = setsockopt(m->llmnr_ipv4_udp_fd, IPPROTO_IP, IP_MTU_DISCOVER, &pmtu, sizeof(pmtu));
if (r < 0) {
r = -errno;
goto fail;
}
r = bind(m->llmnr_ipv4_udp_fd, &sa.sa, sizeof(sa.in));
if (r < 0) {
r = -errno;
goto fail;
}
r = sd_event_add_io(m->event, &m->llmnr_ipv4_udp_event_source, m->llmnr_ipv4_udp_fd, EPOLLIN, on_llmnr_packet, m);
if (r < 0)
goto fail;
return m->llmnr_ipv4_udp_fd;
fail:
m->llmnr_ipv4_udp_fd = safe_close(m->llmnr_ipv4_udp_fd);
return r;
}
int manager_llmnr_ipv6_udp_fd(Manager *m) {
union sockaddr_union sa = {
.in6.sin6_family = AF_INET6,
.in6.sin6_port = htobe16(5355),
};
static const int one = 1, ttl = 255;
int r;
assert(m);
if (m->llmnr_ipv6_udp_fd >= 0)
return m->llmnr_ipv6_udp_fd;
m->llmnr_ipv6_udp_fd = socket(AF_INET6, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
if (m->llmnr_ipv6_udp_fd < 0)
return -errno;
r = setsockopt(m->llmnr_ipv6_udp_fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl));
if (r < 0) {
r = -errno;
goto fail;
}
/* RFC 4795, section 2.5 recommends setting the TTL of UDP packets to 255. */
r = setsockopt(m->llmnr_ipv6_udp_fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &ttl, sizeof(ttl));
if (r < 0) {
r = -errno;
goto fail;
}
r = setsockopt(m->llmnr_ipv6_udp_fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &one, sizeof(one));
if (r < 0) {
r = -errno;
goto fail;
}
r = setsockopt(m->llmnr_ipv6_udp_fd, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one));
if (r < 0) {
r = -errno;
goto fail;
}
r = setsockopt(m->llmnr_ipv6_udp_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
if (r < 0) {
r = -errno;
goto fail;
}
r = setsockopt(m->llmnr_ipv6_udp_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &one, sizeof(one));
if (r < 0) {
r = -errno;
goto fail;
}
r = setsockopt(m->llmnr_ipv6_udp_fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &one, sizeof(one));
if (r < 0) {
r = -errno;
goto fail;
}
r = bind(m->llmnr_ipv6_udp_fd, &sa.sa, sizeof(sa.in6));
if (r < 0) {
r = -errno;
goto fail;
}
r = sd_event_add_io(m->event, &m->llmnr_ipv6_udp_event_source, m->llmnr_ipv6_udp_fd, EPOLLIN, on_llmnr_packet, m);
if (r < 0) {
r = -errno;
goto fail;
}
return m->llmnr_ipv6_udp_fd;
fail:
m->llmnr_ipv6_udp_fd = safe_close(m->llmnr_ipv6_udp_fd);
return r;
}
static int on_llmnr_stream_packet(DnsStream *s) {
assert(s);
if (dns_packet_validate_query(s->read_packet) > 0) {
Link *l;
l = hashmap_get(s->manager->links, INT_TO_PTR(s->read_packet->ifindex));
if (l) {
DnsScope *scope = NULL;
if (s->read_packet->family == AF_INET)
scope = l->llmnr_ipv4_scope;
else if (s->read_packet->family == AF_INET6)
scope = l->llmnr_ipv6_scope;
if (scope) {
dns_scope_process_query(scope, s, s->read_packet);
/* If no reply packet was set, we free the stream */
if (s->write_packet)
return 0;
}
}
}
dns_stream_free(s);
return 0;
}
static int on_llmnr_stream(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
DnsStream *stream;
Manager *m = userdata;
int cfd, r;
cfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC);
if (cfd < 0) {
if (errno == EAGAIN || errno == EINTR)
return 0;
return -errno;
}
r = dns_stream_new(m, &stream, DNS_PROTOCOL_LLMNR, cfd);
if (r < 0) {
safe_close(cfd);
return r;
}
stream->on_packet = on_llmnr_stream_packet;
return 0;
}
int manager_llmnr_ipv4_tcp_fd(Manager *m) {
union sockaddr_union sa = {
.in.sin_family = AF_INET,
.in.sin_port = htobe16(5355),
};
static const int one = 1, pmtu = IP_PMTUDISC_DONT;
int r;
assert(m);
if (m->llmnr_ipv4_tcp_fd >= 0)
return m->llmnr_ipv4_tcp_fd;
m->llmnr_ipv4_tcp_fd = socket(AF_INET, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
if (m->llmnr_ipv4_tcp_fd < 0)
return -errno;
/* RFC 4795, section 2.5. requires setting the TTL of TCP streams to 1 */
r = setsockopt(m->llmnr_ipv4_tcp_fd, IPPROTO_IP, IP_TTL, &one, sizeof(one));
if (r < 0) {
r = -errno;
goto fail;
}
r = setsockopt(m->llmnr_ipv4_tcp_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
if (r < 0) {
r = -errno;
goto fail;
}
r = setsockopt(m->llmnr_ipv4_tcp_fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof(one));
if (r < 0) {
r = -errno;
goto fail;
}
r = setsockopt(m->llmnr_ipv4_tcp_fd, IPPROTO_IP, IP_RECVTTL, &one, sizeof(one));
if (r < 0) {
r = -errno;
goto fail;
}
/* Disable Don't-Fragment bit in the IP header */
r = setsockopt(m->llmnr_ipv4_tcp_fd, IPPROTO_IP, IP_MTU_DISCOVER, &pmtu, sizeof(pmtu));
if (r < 0) {
r = -errno;
goto fail;
}
r = bind(m->llmnr_ipv4_tcp_fd, &sa.sa, sizeof(sa.in));
if (r < 0) {
r = -errno;
goto fail;
}
r = listen(m->llmnr_ipv4_tcp_fd, SOMAXCONN);
if (r < 0) {
r = -errno;
goto fail;
}
r = sd_event_add_io(m->event, &m->llmnr_ipv4_tcp_event_source, m->llmnr_ipv4_tcp_fd, EPOLLIN, on_llmnr_stream, m);
if (r < 0)
goto fail;
return m->llmnr_ipv4_tcp_fd;
fail:
m->llmnr_ipv4_tcp_fd = safe_close(m->llmnr_ipv4_tcp_fd);
return r;
}
int manager_llmnr_ipv6_tcp_fd(Manager *m) {
union sockaddr_union sa = {
.in6.sin6_family = AF_INET6,
.in6.sin6_port = htobe16(5355),
};
static const int one = 1;
int r;
assert(m);
if (m->llmnr_ipv6_tcp_fd >= 0)
return m->llmnr_ipv6_tcp_fd;
m->llmnr_ipv6_tcp_fd = socket(AF_INET6, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
if (m->llmnr_ipv6_tcp_fd < 0)
return -errno;
/* RFC 4795, section 2.5. requires setting the TTL of TCP streams to 1 */
r = setsockopt(m->llmnr_ipv6_tcp_fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &one, sizeof(one));
if (r < 0) {
r = -errno;
goto fail;
}
r = setsockopt(m->llmnr_ipv6_tcp_fd, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one));
if (r < 0) {
r = -errno;
goto fail;
}
r = setsockopt(m->llmnr_ipv6_tcp_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
if (r < 0) {
r = -errno;
goto fail;
}
r = setsockopt(m->llmnr_ipv6_tcp_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &one, sizeof(one));
if (r < 0) {
r = -errno;
goto fail;
}
r = setsockopt(m->llmnr_ipv6_tcp_fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &one, sizeof(one));
if (r < 0) {
r = -errno;
goto fail;
}
r = bind(m->llmnr_ipv6_tcp_fd, &sa.sa, sizeof(sa.in6));
if (r < 0) {
r = -errno;
goto fail;
}
r = listen(m->llmnr_ipv6_tcp_fd, SOMAXCONN);
if (r < 0) {
r = -errno;
goto fail;
}
r = sd_event_add_io(m->event, &m->llmnr_ipv6_tcp_event_source, m->llmnr_ipv6_tcp_fd, EPOLLIN, on_llmnr_stream, m);
if (r < 0) {
r = -errno;
goto fail;
}
return m->llmnr_ipv6_tcp_fd;
fail:
m->llmnr_ipv6_tcp_fd = safe_close(m->llmnr_ipv6_tcp_fd);
return r;
}
int manager_ifindex_is_loopback(Manager *m, int ifindex) {
Link *l;
assert(m);
if (ifindex <= 0)
return -EINVAL;
l = hashmap_get(m->links, INT_TO_PTR(ifindex));
if (l->flags & IFF_LOOPBACK)
return 1;
return 0;
}
int manager_find_ifindex(Manager *m, int family, const union in_addr_union *in_addr) {
LinkAddress *a;
assert(m);
a = manager_find_address(m, family, in_addr);
if (a)
return a->link->ifindex;
return 0;
}
int manager_next_hostname(Manager *m) {
const char *p;
Iterator i;
uint64_t u;
char *h;
Link *l;
assert(m);
p = strchr(m->hostname, 0);
assert(p);
while (p > m->hostname) {
if (!strchr("0123456789", p[-1]))
break;
p--;
}
if (*p == 0 || safe_atou64(p, &u) < 0 || u <= 0)
u = 1;
u++;
if (asprintf(&h, "%.*s%" PRIu64, (int) (p - m->hostname), m->hostname, u) < 0)
return -ENOMEM;
log_info("Hostname conflict, changing local hostname from '%s' to '%s'.", m->hostname, h);
free(m->hostname);
m->hostname = h;
m->host_ipv4_key = dns_resource_key_unref(m->host_ipv4_key);
m->host_ipv6_key = dns_resource_key_unref(m->host_ipv6_key);
HASHMAP_FOREACH(l, m->links, i) {
link_add_rrs(l, true);
link_add_rrs(l, false);
}
return 0;
}
LinkAddress* manager_find_address(Manager *m, int family, const union in_addr_union *in_addr) {
Iterator i;
Link *l;
assert(m);
HASHMAP_FOREACH(l, m->links, i) {
LinkAddress *a;
a = link_find_address(l, family, in_addr);
if (a)
return a;
}
return NULL;
}
int manager_our_packet(Manager *m, DnsPacket *p) {
assert(m);
assert(p);
return !!manager_find_address(m, p->family, &p->sender);
}