]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/resolve/resolved-mdns.c
resolved: add infrastructure for mDNS related sockets
[thirdparty/systemd.git] / src / resolve / resolved-mdns.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2015 Daniel Mack
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <resolv.h>
23 #include <netinet/in.h>
24 #include <arpa/inet.h>
25
26 #include "fd-util.h"
27 #include "resolved-manager.h"
28 #include "resolved-mdns.h"
29
30 void manager_mdns_stop(Manager *m) {
31 assert(m);
32
33 m->mdns_ipv4_event_source = sd_event_source_unref(m->mdns_ipv4_event_source);
34 m->mdns_ipv4_fd = safe_close(m->mdns_ipv4_fd);
35
36 m->mdns_ipv6_event_source = sd_event_source_unref(m->mdns_ipv6_event_source);
37 m->mdns_ipv6_fd = safe_close(m->mdns_ipv6_fd);
38 }
39
40 int manager_mdns_start(Manager *m) {
41 int r;
42
43 assert(m);
44
45 if (m->mdns_support == SUPPORT_NO)
46 return 0;
47
48 r = manager_mdns_ipv4_fd(m);
49 if (r == -EADDRINUSE)
50 goto eaddrinuse;
51 if (r < 0)
52 return r;
53
54 if (socket_ipv6_is_supported()) {
55 r = manager_mdns_ipv6_fd(m);
56 if (r == -EADDRINUSE)
57 goto eaddrinuse;
58 if (r < 0)
59 return r;
60 }
61
62 return 0;
63
64 eaddrinuse:
65 log_warning("There appears to be another mDNS responder running. Turning off mDNS support.");
66 m->mdns_support = SUPPORT_NO;
67 manager_mdns_stop(m);
68
69 return 0;
70 }
71
72 static int on_mdns_packet(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
73 /* TODO */
74 return 0;
75 }
76
77 int manager_mdns_ipv4_fd(Manager *m) {
78 union sockaddr_union sa = {
79 .in.sin_family = AF_INET,
80 .in.sin_port = htobe16(MDNS_PORT),
81 };
82 static const int one = 1, pmtu = IP_PMTUDISC_DONT, ttl = 255;
83 int r;
84
85 assert(m);
86
87 if (m->mdns_ipv4_fd >= 0)
88 return m->mdns_ipv4_fd;
89
90 m->mdns_ipv4_fd = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
91 if (m->mdns_ipv4_fd < 0)
92 return -errno;
93
94 r = setsockopt(m->mdns_ipv4_fd, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl));
95 if (r < 0) {
96 r = -errno;
97 goto fail;
98 }
99
100 r = setsockopt(m->mdns_ipv4_fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl));
101 if (r < 0) {
102 r = -errno;
103 goto fail;
104 }
105
106 r = setsockopt(m->mdns_ipv4_fd, IPPROTO_IP, IP_MULTICAST_LOOP, &one, sizeof(one));
107 if (r < 0) {
108 r = -errno;
109 goto fail;
110 }
111
112 r = setsockopt(m->mdns_ipv4_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
113 if (r < 0) {
114 r = -errno;
115 goto fail;
116 }
117
118 r = setsockopt(m->mdns_ipv4_fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof(one));
119 if (r < 0) {
120 r = -errno;
121 goto fail;
122 }
123
124 r = setsockopt(m->mdns_ipv4_fd, IPPROTO_IP, IP_RECVTTL, &one, sizeof(one));
125 if (r < 0) {
126 r = -errno;
127 goto fail;
128 }
129
130 /* Disable Don't-Fragment bit in the IP header */
131 r = setsockopt(m->mdns_ipv4_fd, IPPROTO_IP, IP_MTU_DISCOVER, &pmtu, sizeof(pmtu));
132 if (r < 0) {
133 r = -errno;
134 goto fail;
135 }
136
137 r = bind(m->mdns_ipv4_fd, &sa.sa, sizeof(sa.in));
138 if (r < 0) {
139 r = -errno;
140 goto fail;
141 }
142
143 r = sd_event_add_io(m->event, &m->mdns_ipv4_event_source, m->mdns_ipv4_fd, EPOLLIN, on_mdns_packet, m);
144 if (r < 0)
145 goto fail;
146
147 return m->mdns_ipv4_fd;
148
149 fail:
150 m->mdns_ipv4_fd = safe_close(m->mdns_ipv4_fd);
151 return r;
152 }
153
154 int manager_mdns_ipv6_fd(Manager *m) {
155 union sockaddr_union sa = {
156 .in6.sin6_family = AF_INET6,
157 .in6.sin6_port = htobe16(MDNS_PORT),
158 };
159 static const int one = 1, ttl = 255;
160 int r;
161
162 assert(m);
163
164 if (m->mdns_ipv6_fd >= 0)
165 return m->mdns_ipv6_fd;
166
167 m->mdns_ipv6_fd = socket(AF_INET6, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
168 if (m->mdns_ipv6_fd < 0)
169 return -errno;
170
171 r = setsockopt(m->mdns_ipv6_fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl));
172 if (r < 0) {
173 r = -errno;
174 goto fail;
175 }
176
177 /* RFC 4795, section 2.5 recommends setting the TTL of UDP packets to 255. */
178 r = setsockopt(m->mdns_ipv6_fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &ttl, sizeof(ttl));
179 if (r < 0) {
180 r = -errno;
181 goto fail;
182 }
183
184 r = setsockopt(m->mdns_ipv6_fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &one, sizeof(one));
185 if (r < 0) {
186 r = -errno;
187 goto fail;
188 }
189
190 r = setsockopt(m->mdns_ipv6_fd, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one));
191 if (r < 0) {
192 r = -errno;
193 goto fail;
194 }
195
196 r = setsockopt(m->mdns_ipv6_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
197 if (r < 0) {
198 r = -errno;
199 goto fail;
200 }
201
202 r = setsockopt(m->mdns_ipv6_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &one, sizeof(one));
203 if (r < 0) {
204 r = -errno;
205 goto fail;
206 }
207
208 r = setsockopt(m->mdns_ipv6_fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &one, sizeof(one));
209 if (r < 0) {
210 r = -errno;
211 goto fail;
212 }
213
214 r = bind(m->mdns_ipv6_fd, &sa.sa, sizeof(sa.in6));
215 if (r < 0) {
216 r = -errno;
217 goto fail;
218 }
219
220 r = sd_event_add_io(m->event, &m->mdns_ipv6_event_source, m->mdns_ipv6_fd, EPOLLIN, on_mdns_packet, m);
221 if (r < 0) {
222 r = -errno;
223 goto fail;
224 }
225
226 return m->mdns_ipv6_fd;
227
228 fail:
229 m->mdns_ipv6_fd = safe_close(m->mdns_ipv6_fd);
230 return r;
231 }