]>
Commit | Line | Data |
---|---|---|
12e41d03 DL |
1 | /* |
2 | PIM for Quagga | |
3 | Copyright (C) 2008 Everton da Silva Marques | |
4 | ||
5 | This program is free software; you can redistribute it and/or modify | |
6 | it under the terms of the GNU General Public License as published by | |
7 | the Free Software Foundation; either version 2 of the License, or | |
8 | (at your option) any later version. | |
9 | ||
10 | This program is distributed in the hope that it will be useful, but | |
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
13 | General Public License for more details. | |
14 | ||
15 | You should have received a copy of the GNU General Public License | |
16 | along with this program; see the file COPYING; if not, write to the | |
17 | Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, | |
18 | MA 02110-1301 USA | |
19 | ||
20 | $QuaggaId: $Format:%an, %ai, %h$ $ | |
21 | */ | |
22 | ||
23 | #include <zebra.h> | |
24 | #include "log.h" | |
25 | #include "privs.h" | |
26 | ||
27 | #include "pimd.h" | |
28 | #include "pim_mroute.h" | |
29 | #include "pim_str.h" | |
30 | #include "pim_time.h" | |
31 | #include "pim_iface.h" | |
32 | #include "pim_macro.h" | |
33 | ||
34 | /* GLOBAL VARS */ | |
35 | extern struct zebra_privs_t pimd_privs; | |
36 | ||
37 | static void mroute_read_on(void); | |
38 | ||
39 | static int pim_mroute_set(int fd, int enable) | |
40 | { | |
41 | int err; | |
42 | int opt = enable ? MRT_INIT : MRT_DONE; | |
43 | socklen_t opt_len = sizeof(opt); | |
44 | ||
45 | err = setsockopt(fd, IPPROTO_IP, opt, &opt, opt_len); | |
46 | if (err) { | |
47 | int e = errno; | |
48 | zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,%s=%d): errno=%d: %s", | |
49 | __FILE__, __PRETTY_FUNCTION__, | |
50 | fd, enable ? "MRT_INIT" : "MRT_DONE", opt, e, safe_strerror(e)); | |
51 | errno = e; | |
52 | return -1; | |
53 | } | |
54 | ||
55 | #if 0 | |
56 | zlog_info("%s %s: setsockopt(fd=%d,IPPROTO_IP,MRT_INIT,opt=%d): ok", | |
57 | __FILE__, __PRETTY_FUNCTION__, | |
58 | fd, opt); | |
59 | #endif | |
60 | ||
61 | return 0; | |
62 | } | |
63 | ||
64 | int pim_mroute_msg(int fd, const char *buf, int buf_size) | |
65 | { | |
66 | struct interface *ifp; | |
67 | const struct ip *ip_hdr; | |
68 | const struct igmpmsg *msg; | |
69 | const char *upcall; | |
70 | char src_str[100]; | |
71 | char grp_str[100]; | |
72 | ||
73 | ip_hdr = (const struct ip *) buf; | |
74 | ||
75 | /* kernel upcall must have protocol=0 */ | |
76 | if (ip_hdr->ip_p) { | |
77 | /* this is not a kernel upcall */ | |
78 | #ifdef PIM_UNEXPECTED_KERNEL_UPCALL | |
79 | zlog_warn("%s: not a kernel upcall proto=%d msg_size=%d", | |
80 | __PRETTY_FUNCTION__, ip_hdr->ip_p, buf_size); | |
81 | #endif | |
82 | return 0; | |
83 | } | |
84 | ||
85 | msg = (const struct igmpmsg *) buf; | |
86 | ||
87 | switch (msg->im_msgtype) { | |
88 | case IGMPMSG_NOCACHE: upcall = "NOCACHE"; break; | |
89 | case IGMPMSG_WRONGVIF: upcall = "WRONGVIF"; break; | |
90 | case IGMPMSG_WHOLEPKT: upcall = "WHOLEPKT"; break; | |
91 | default: upcall = "<unknown_upcall?>"; | |
92 | } | |
93 | ifp = pim_if_find_by_vif_index(msg->im_vif); | |
94 | pim_inet4_dump("<src?>", msg->im_src, src_str, sizeof(src_str)); | |
95 | pim_inet4_dump("<grp?>", msg->im_dst, grp_str, sizeof(grp_str)); | |
96 | ||
97 | if (msg->im_msgtype == IGMPMSG_WRONGVIF) { | |
98 | struct pim_ifchannel *ch; | |
99 | struct pim_interface *pim_ifp; | |
100 | ||
101 | /* | |
102 | Send Assert(S,G) on iif as response to WRONGVIF kernel upcall. | |
103 | ||
104 | RFC 4601 4.8.2. PIM-SSM-Only Routers | |
105 | ||
106 | iif is the incoming interface of the packet. | |
107 | if (iif is in inherited_olist(S,G)) { | |
108 | send Assert(S,G) on iif | |
109 | } | |
110 | */ | |
111 | ||
112 | if (PIM_DEBUG_PIM_TRACE) { | |
113 | zlog_debug("%s: WRONGVIF from fd=%d for (S,G)=(%s,%s) on %s vifi=%d", | |
114 | __PRETTY_FUNCTION__, | |
115 | fd, | |
116 | src_str, | |
117 | grp_str, | |
118 | ifp ? ifp->name : "<ifname?>", | |
119 | msg->im_vif); | |
120 | } | |
121 | ||
122 | if (!ifp) { | |
123 | zlog_warn("%s: WRONGVIF (S,G)=(%s,%s) could not find input interface for input_vif_index=%d", | |
124 | __PRETTY_FUNCTION__, | |
125 | src_str, grp_str, msg->im_vif); | |
126 | return -1; | |
127 | } | |
128 | ||
129 | pim_ifp = ifp->info; | |
130 | if (!pim_ifp) { | |
131 | zlog_warn("%s: WRONGVIF (S,G)=(%s,%s) multicast not enabled on interface %s", | |
132 | __PRETTY_FUNCTION__, | |
133 | src_str, grp_str, ifp->name); | |
134 | return -2; | |
135 | } | |
136 | ||
137 | ch = pim_ifchannel_find(ifp, msg->im_src, msg->im_dst); | |
138 | if (!ch) { | |
139 | zlog_warn("%s: WRONGVIF (S,G)=(%s,%s) could not find channel on interface %s", | |
140 | __PRETTY_FUNCTION__, | |
141 | src_str, grp_str, ifp->name); | |
142 | return -3; | |
143 | } | |
144 | ||
145 | /* | |
146 | RFC 4601: 4.6.1. (S,G) Assert Message State Machine | |
147 | ||
148 | Transitions from NoInfo State | |
149 | ||
150 | An (S,G) data packet arrives on interface I, AND | |
151 | CouldAssert(S,G,I)==TRUE An (S,G) data packet arrived on an | |
152 | downstream interface that is in our (S,G) outgoing interface | |
153 | list. We optimistically assume that we will be the assert | |
154 | winner for this (S,G), and so we transition to the "I am Assert | |
155 | Winner" state and perform Actions A1 (below), which will | |
156 | initiate the assert negotiation for (S,G). | |
157 | */ | |
158 | ||
159 | if (ch->ifassert_state != PIM_IFASSERT_NOINFO) { | |
160 | zlog_warn("%s: WRONGVIF (S,G)=(%s,%s) channel is not on Assert NoInfo state for interface %s", | |
161 | __PRETTY_FUNCTION__, | |
162 | src_str, grp_str, ifp->name); | |
163 | return -4; | |
164 | } | |
165 | ||
166 | if (!PIM_IF_FLAG_TEST_COULD_ASSERT(ch->flags)) { | |
167 | zlog_warn("%s: WRONGVIF (S,G)=(%s,%s) interface %s is not downstream for channel", | |
168 | __PRETTY_FUNCTION__, | |
169 | src_str, grp_str, ifp->name); | |
170 | return -5; | |
171 | } | |
172 | ||
173 | if (assert_action_a1(ch)) { | |
174 | zlog_warn("%s: WRONGVIF (S,G)=(%s,%s) assert_action_a1 failure on interface %s", | |
175 | __PRETTY_FUNCTION__, | |
176 | src_str, grp_str, ifp->name); | |
177 | return -6; | |
178 | } | |
179 | ||
180 | return 0; | |
181 | } /* IGMPMSG_WRONGVIF */ | |
182 | ||
183 | zlog_warn("%s: kernel upcall %s type=%d ip_p=%d from fd=%d for (S,G)=(%s,%s) on %s vifi=%d", | |
184 | __PRETTY_FUNCTION__, | |
185 | upcall, | |
186 | msg->im_msgtype, | |
187 | ip_hdr->ip_p, | |
188 | fd, | |
189 | src_str, | |
190 | grp_str, | |
191 | ifp ? ifp->name : "<ifname?>", | |
192 | msg->im_vif); | |
193 | ||
194 | return 0; | |
195 | } | |
196 | ||
197 | static int mroute_read_msg(int fd) | |
198 | { | |
199 | const int msg_min_size = MAX(sizeof(struct ip), sizeof(struct igmpmsg)); | |
200 | char buf[1000]; | |
201 | int rd; | |
202 | ||
203 | if (((int) sizeof(buf)) < msg_min_size) { | |
204 | zlog_err("%s: fd=%d: buf size=%zu lower than msg_min=%d", | |
205 | __PRETTY_FUNCTION__, fd, sizeof(buf), msg_min_size); | |
206 | return -1; | |
207 | } | |
208 | ||
209 | rd = read(fd, buf, sizeof(buf)); | |
210 | if (rd < 0) { | |
211 | zlog_warn("%s: failure reading fd=%d: errno=%d: %s", | |
212 | __PRETTY_FUNCTION__, fd, errno, safe_strerror(errno)); | |
213 | return -2; | |
214 | } | |
215 | ||
216 | if (rd < msg_min_size) { | |
217 | zlog_warn("%s: short message reading fd=%d: read=%d msg_min=%d", | |
218 | __PRETTY_FUNCTION__, fd, rd, msg_min_size); | |
219 | return -3; | |
220 | } | |
221 | ||
222 | return pim_mroute_msg(fd, buf, rd); | |
223 | } | |
224 | ||
225 | static int mroute_read(struct thread *t) | |
226 | { | |
227 | int fd; | |
228 | int result; | |
229 | ||
230 | zassert(t); | |
231 | zassert(!THREAD_ARG(t)); | |
232 | ||
233 | fd = THREAD_FD(t); | |
234 | zassert(fd == qpim_mroute_socket_fd); | |
235 | ||
236 | result = mroute_read_msg(fd); | |
237 | ||
238 | /* Keep reading */ | |
239 | qpim_mroute_socket_reader = 0; | |
240 | mroute_read_on(); | |
241 | ||
242 | return result; | |
243 | } | |
244 | ||
245 | static void mroute_read_on() | |
246 | { | |
247 | zassert(!qpim_mroute_socket_reader); | |
248 | zassert(PIM_MROUTE_IS_ENABLED); | |
249 | ||
250 | THREAD_READ_ON(master, qpim_mroute_socket_reader, | |
251 | mroute_read, 0, qpim_mroute_socket_fd); | |
252 | } | |
253 | ||
254 | static void mroute_read_off() | |
255 | { | |
256 | THREAD_OFF(qpim_mroute_socket_reader); | |
257 | } | |
258 | ||
259 | int pim_mroute_socket_enable() | |
260 | { | |
261 | int fd; | |
b45cefcb | 262 | struct in_addr pimreg = { .s_addr = 0 }; |
12e41d03 DL |
263 | |
264 | if (PIM_MROUTE_IS_ENABLED) | |
265 | return -1; | |
266 | ||
267 | if ( pimd_privs.change (ZPRIVS_RAISE) ) | |
268 | zlog_err ("pim_mroute_socket_enable: could not raise privs, %s", | |
269 | safe_strerror (errno) ); | |
270 | ||
271 | fd = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP); | |
272 | ||
273 | if ( pimd_privs.change (ZPRIVS_LOWER) ) | |
274 | zlog_err ("pim_mroute_socket_enable: could not lower privs, %s", | |
275 | safe_strerror (errno) ); | |
276 | ||
277 | if (fd < 0) { | |
278 | zlog_warn("Could not create mroute socket: errno=%d: %s", | |
279 | errno, safe_strerror(errno)); | |
280 | return -2; | |
281 | } | |
282 | ||
283 | if (pim_mroute_set(fd, 1)) { | |
284 | zlog_warn("Could not enable mroute on socket fd=%d: errno=%d: %s", | |
285 | fd, errno, safe_strerror(errno)); | |
286 | close(fd); | |
287 | return -3; | |
288 | } | |
289 | ||
290 | qpim_mroute_socket_fd = fd; | |
b45cefcb DS |
291 | pim_mroute_add_vif(MAXVIFS-1, pimreg, VIFF_REGISTER); |
292 | ||
12e41d03 DL |
293 | qpim_mroute_socket_creation = pim_time_monotonic_sec(); |
294 | mroute_read_on(); | |
295 | ||
296 | zassert(PIM_MROUTE_IS_ENABLED); | |
297 | ||
298 | return 0; | |
299 | } | |
300 | ||
301 | int pim_mroute_socket_disable() | |
302 | { | |
303 | if (PIM_MROUTE_IS_DISABLED) | |
304 | return -1; | |
305 | ||
306 | if (pim_mroute_set(qpim_mroute_socket_fd, 0)) { | |
307 | zlog_warn("Could not disable mroute on socket fd=%d: errno=%d: %s", | |
308 | qpim_mroute_socket_fd, errno, safe_strerror(errno)); | |
309 | return -2; | |
310 | } | |
311 | ||
312 | if (close(qpim_mroute_socket_fd)) { | |
313 | zlog_warn("Failure closing mroute socket: fd=%d errno=%d: %s", | |
314 | qpim_mroute_socket_fd, errno, safe_strerror(errno)); | |
315 | return -3; | |
316 | } | |
317 | ||
318 | mroute_read_off(); | |
319 | qpim_mroute_socket_fd = -1; | |
320 | ||
321 | zassert(PIM_MROUTE_IS_DISABLED); | |
322 | ||
323 | return 0; | |
324 | } | |
325 | ||
326 | /* | |
327 | For each network interface (e.g., physical or a virtual tunnel) that | |
328 | would be used for multicast forwarding, a corresponding multicast | |
329 | interface must be added to the kernel. | |
330 | */ | |
b45cefcb | 331 | int pim_mroute_add_vif(int vif_index, struct in_addr ifaddr, unsigned char flags) |
12e41d03 DL |
332 | { |
333 | struct vifctl vc; | |
334 | int err; | |
335 | ||
336 | if (PIM_MROUTE_IS_DISABLED) { | |
337 | zlog_warn("%s: global multicast is disabled", | |
338 | __PRETTY_FUNCTION__); | |
339 | return -1; | |
340 | } | |
341 | ||
342 | memset(&vc, 0, sizeof(vc)); | |
343 | vc.vifc_vifi = vif_index; | |
b45cefcb | 344 | vc.vifc_flags = flags; |
12e41d03 DL |
345 | vc.vifc_threshold = PIM_MROUTE_MIN_TTL; |
346 | vc.vifc_rate_limit = 0; | |
347 | memcpy(&vc.vifc_lcl_addr, &ifaddr, sizeof(vc.vifc_lcl_addr)); | |
348 | ||
349 | #ifdef PIM_DVMRP_TUNNEL | |
350 | if (vc.vifc_flags & VIFF_TUNNEL) { | |
351 | memcpy(&vc.vifc_rmt_addr, &vif_remote_addr, sizeof(vc.vifc_rmt_addr)); | |
352 | } | |
353 | #endif | |
354 | ||
b45cefcb | 355 | err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_ADD_VIF, (void*) &vc, sizeof(vc)); |
12e41d03 DL |
356 | if (err) { |
357 | char ifaddr_str[100]; | |
358 | int e = errno; | |
359 | ||
360 | pim_inet4_dump("<ifaddr?>", ifaddr, ifaddr_str, sizeof(ifaddr_str)); | |
361 | ||
362 | zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_VIF,vif_index=%d,ifaddr=%s): errno=%d: %s", | |
363 | __FILE__, __PRETTY_FUNCTION__, | |
364 | qpim_mroute_socket_fd, vif_index, ifaddr_str, | |
365 | e, safe_strerror(e)); | |
366 | errno = e; | |
367 | return -2; | |
368 | } | |
369 | ||
370 | return 0; | |
371 | } | |
372 | ||
373 | int pim_mroute_del_vif(int vif_index) | |
374 | { | |
375 | struct vifctl vc; | |
376 | int err; | |
377 | ||
378 | if (PIM_MROUTE_IS_DISABLED) { | |
379 | zlog_warn("%s: global multicast is disabled", | |
380 | __PRETTY_FUNCTION__); | |
381 | return -1; | |
382 | } | |
383 | ||
384 | memset(&vc, 0, sizeof(vc)); | |
385 | vc.vifc_vifi = vif_index; | |
386 | ||
387 | err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_DEL_VIF, (void*) &vc, sizeof(vc)); | |
388 | if (err) { | |
389 | int e = errno; | |
390 | zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_VIF,vif_index=%d): errno=%d: %s", | |
391 | __FILE__, __PRETTY_FUNCTION__, | |
392 | qpim_mroute_socket_fd, vif_index, | |
393 | e, safe_strerror(e)); | |
394 | errno = e; | |
395 | return -2; | |
396 | } | |
397 | ||
398 | return 0; | |
399 | } | |
400 | ||
401 | int pim_mroute_add(struct mfcctl *mc) | |
402 | { | |
403 | int err; | |
404 | ||
405 | qpim_mroute_add_last = pim_time_monotonic_sec(); | |
406 | ++qpim_mroute_add_events; | |
407 | ||
408 | if (PIM_MROUTE_IS_DISABLED) { | |
409 | zlog_warn("%s: global multicast is disabled", | |
410 | __PRETTY_FUNCTION__); | |
411 | return -1; | |
412 | } | |
413 | ||
414 | err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_ADD_MFC, | |
415 | mc, sizeof(*mc)); | |
416 | if (err) { | |
417 | int e = errno; | |
418 | zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_MFC): errno=%d: %s", | |
419 | __FILE__, __PRETTY_FUNCTION__, | |
420 | qpim_mroute_socket_fd, | |
421 | e, safe_strerror(e)); | |
422 | errno = e; | |
423 | return -2; | |
424 | } | |
425 | ||
426 | return 0; | |
427 | } | |
428 | ||
429 | int pim_mroute_del(struct mfcctl *mc) | |
430 | { | |
431 | int err; | |
432 | ||
433 | qpim_mroute_del_last = pim_time_monotonic_sec(); | |
434 | ++qpim_mroute_del_events; | |
435 | ||
436 | if (PIM_MROUTE_IS_DISABLED) { | |
437 | zlog_warn("%s: global multicast is disabled", | |
438 | __PRETTY_FUNCTION__); | |
439 | return -1; | |
440 | } | |
441 | ||
442 | err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_DEL_MFC, mc, sizeof(*mc)); | |
443 | if (err) { | |
444 | int e = errno; | |
445 | zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_MFC): errno=%d: %s", | |
446 | __FILE__, __PRETTY_FUNCTION__, | |
447 | qpim_mroute_socket_fd, | |
448 | e, safe_strerror(e)); | |
449 | errno = e; | |
450 | return -2; | |
451 | } | |
452 | ||
453 | return 0; | |
454 | } |