]> git.sommitrealweird.co.uk Git - quagga-debian.git/blob - pimd/pim_mroute.c
New upstream release and new maintainer
[quagga-debian.git] / pimd / pim_mroute.c
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;
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
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  */
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
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
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
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 }