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 
pim_mroute_set(int fd,int enable)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 
pim_mroute_msg(int fd,const char * buf,int buf_size)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 
mroute_read_msg(int fd)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 
mroute_read(struct thread * t)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 
mroute_read_on()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 
mroute_read_off()254 static void mroute_read_off()
255 {
256   THREAD_OFF(qpim_mroute_socket_reader);
257 }
258 
pim_mroute_socket_enable()259 int pim_mroute_socket_enable()
260 {
261   int fd;
262 
263   if (PIM_MROUTE_IS_ENABLED)
264     return -1;
265 
266   if ( pimd_privs.change (ZPRIVS_RAISE) )
267     zlog_err ("pim_mroute_socket_enable: could not raise privs, %s",
268               safe_strerror (errno) );
269 
270   fd = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP);
271 
272   if ( pimd_privs.change (ZPRIVS_LOWER) )
273     zlog_err ("pim_mroute_socket_enable: could not lower privs, %s",
274 	      safe_strerror (errno) );
275 
276   if (fd < 0) {
277     zlog_warn("Could not create mroute socket: errno=%d: %s",
278 	      errno, safe_strerror(errno));
279     return -2;
280   }
281 
282   if (pim_mroute_set(fd, 1)) {
283     zlog_warn("Could not enable mroute on socket fd=%d: errno=%d: %s",
284 	      fd, errno, safe_strerror(errno));
285     close(fd);
286     return -3;
287   }
288 
289   qpim_mroute_socket_fd       = fd;
290   qpim_mroute_socket_creation = pim_time_monotonic_sec();
291   mroute_read_on();
292 
293   zassert(PIM_MROUTE_IS_ENABLED);
294 
295   return 0;
296 }
297 
pim_mroute_socket_disable()298 int pim_mroute_socket_disable()
299 {
300   if (PIM_MROUTE_IS_DISABLED)
301     return -1;
302 
303   if (pim_mroute_set(qpim_mroute_socket_fd, 0)) {
304     zlog_warn("Could not disable mroute on socket fd=%d: errno=%d: %s",
305 	      qpim_mroute_socket_fd, errno, safe_strerror(errno));
306     return -2;
307   }
308 
309   if (close(qpim_mroute_socket_fd)) {
310     zlog_warn("Failure closing mroute socket: fd=%d errno=%d: %s",
311 	      qpim_mroute_socket_fd, errno, safe_strerror(errno));
312     return -3;
313   }
314 
315   mroute_read_off();
316   qpim_mroute_socket_fd = -1;
317 
318   zassert(PIM_MROUTE_IS_DISABLED);
319 
320   return 0;
321 }
322 
323 /*
324   For each network interface (e.g., physical or a virtual tunnel) that
325   would be used for multicast forwarding, a corresponding multicast
326   interface must be added to the kernel.
327  */
pim_mroute_add_vif(int vif_index,struct in_addr ifaddr)328 int pim_mroute_add_vif(int vif_index, struct in_addr ifaddr)
329 {
330   struct vifctl vc;
331   int err;
332 
333   if (PIM_MROUTE_IS_DISABLED) {
334     zlog_warn("%s: global multicast is disabled",
335 	      __PRETTY_FUNCTION__);
336     return -1;
337   }
338 
339   memset(&vc, 0, sizeof(vc));
340   vc.vifc_vifi = vif_index;
341   vc.vifc_flags = 0;
342   vc.vifc_threshold = PIM_MROUTE_MIN_TTL;
343   vc.vifc_rate_limit = 0;
344   memcpy(&vc.vifc_lcl_addr, &ifaddr, sizeof(vc.vifc_lcl_addr));
345 
346 #ifdef PIM_DVMRP_TUNNEL
347   if (vc.vifc_flags & VIFF_TUNNEL) {
348     memcpy(&vc.vifc_rmt_addr, &vif_remote_addr, sizeof(vc.vifc_rmt_addr));
349   }
350 #endif
351 
352   err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_ADD_VIF, (void*) &vc, sizeof(vc));
353   if (err) {
354     char ifaddr_str[100];
355     int e = errno;
356 
357     pim_inet4_dump("<ifaddr?>", ifaddr, ifaddr_str, sizeof(ifaddr_str));
358 
359     zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_VIF,vif_index=%d,ifaddr=%s): errno=%d: %s",
360 	      __FILE__, __PRETTY_FUNCTION__,
361 	      qpim_mroute_socket_fd, vif_index, ifaddr_str,
362 	      e, safe_strerror(e));
363     errno = e;
364     return -2;
365   }
366 
367   return 0;
368 }
369 
pim_mroute_del_vif(int vif_index)370 int pim_mroute_del_vif(int vif_index)
371 {
372   struct vifctl vc;
373   int err;
374 
375   if (PIM_MROUTE_IS_DISABLED) {
376     zlog_warn("%s: global multicast is disabled",
377 	      __PRETTY_FUNCTION__);
378     return -1;
379   }
380 
381   memset(&vc, 0, sizeof(vc));
382   vc.vifc_vifi = vif_index;
383 
384   err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_DEL_VIF, (void*) &vc, sizeof(vc));
385   if (err) {
386     int e = errno;
387     zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_VIF,vif_index=%d): errno=%d: %s",
388 	      __FILE__, __PRETTY_FUNCTION__,
389 	      qpim_mroute_socket_fd, vif_index,
390 	      e, safe_strerror(e));
391     errno = e;
392     return -2;
393   }
394 
395   return 0;
396 }
397 
pim_mroute_add(struct mfcctl * mc)398 int pim_mroute_add(struct mfcctl *mc)
399 {
400   int err;
401 
402   qpim_mroute_add_last = pim_time_monotonic_sec();
403   ++qpim_mroute_add_events;
404 
405   if (PIM_MROUTE_IS_DISABLED) {
406     zlog_warn("%s: global multicast is disabled",
407 	      __PRETTY_FUNCTION__);
408     return -1;
409   }
410 
411   err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_ADD_MFC,
412 		   mc, sizeof(*mc));
413   if (err) {
414     int e = errno;
415     zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_ADD_MFC): errno=%d: %s",
416 	      __FILE__, __PRETTY_FUNCTION__,
417 	      qpim_mroute_socket_fd,
418 	      e, safe_strerror(e));
419     errno = e;
420     return -2;
421   }
422 
423   return 0;
424 }
425 
pim_mroute_del(struct mfcctl * mc)426 int pim_mroute_del(struct mfcctl *mc)
427 {
428   int err;
429 
430   qpim_mroute_del_last = pim_time_monotonic_sec();
431   ++qpim_mroute_del_events;
432 
433   if (PIM_MROUTE_IS_DISABLED) {
434     zlog_warn("%s: global multicast is disabled",
435 	      __PRETTY_FUNCTION__);
436     return -1;
437   }
438 
439   err = setsockopt(qpim_mroute_socket_fd, IPPROTO_IP, MRT_DEL_MFC, mc, sizeof(*mc));
440   if (err) {
441     int e = errno;
442     zlog_warn("%s %s: failure: setsockopt(fd=%d,IPPROTO_IP,MRT_DEL_MFC): errno=%d: %s",
443 	      __FILE__, __PRETTY_FUNCTION__,
444 	      qpim_mroute_socket_fd,
445 	      e, safe_strerror(e));
446     errno = e;
447     return -2;
448   }
449 
450   return 0;
451 }
452