Annotation of embedaddon/quagga/pimd/pim_mroute.c, revision 1.1
1.1 ! misho 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: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>