1 /* $OpenBSD: ip6_mroute.c,v 1.136 2023/04/19 20:03:52 kn Exp $ */ 2 /* $NetBSD: ip6_mroute.c,v 1.59 2003/12/10 09:28:38 itojun Exp $ */ 3 /* $KAME: ip6_mroute.c,v 1.45 2001/03/25 08:38:51 itojun Exp $ */ 4 5 /* 6 * Copyright (C) 1998 WIDE Project. 7 * All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. Neither the name of the project nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34 /* BSDI ip_mroute.c,v 2.10 1996/11/14 00:29:52 jch Exp */ 35 36 /* 37 * Copyright (c) 1989 Stephen Deering 38 * Copyright (c) 1992, 1993 39 * The Regents of the University of California. All rights reserved. 40 * 41 * This code is derived from software contributed to Berkeley by 42 * Stephen Deering of Stanford University. 43 * 44 * Redistribution and use in source and binary forms, with or without 45 * modification, are permitted provided that the following conditions 46 * are met: 47 * 1. Redistributions of source code must retain the above copyright 48 * notice, this list of conditions and the following disclaimer. 49 * 2. Redistributions in binary form must reproduce the above copyright 50 * notice, this list of conditions and the following disclaimer in the 51 * documentation and/or other materials provided with the distribution. 52 * 3. Neither the name of the University nor the names of its contributors 53 * may be used to endorse or promote products derived from this software 54 * without specific prior written permission. 55 * 56 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 57 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 58 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 59 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 60 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 61 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 62 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 63 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 64 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 65 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 66 * SUCH DAMAGE. 67 * 68 * @(#)ip_mroute.c 8.2 (Berkeley) 11/15/93 69 */ 70 71 /* 72 * IP multicast forwarding procedures 73 * 74 * Written by David Waitzman, BBN Labs, August 1988. 75 * Modified by Steve Deering, Stanford, February 1989. 76 * Modified by Mark J. Steiglitz, Stanford, May, 1991 77 * Modified by Van Jacobson, LBL, January 1993 78 * Modified by Ajit Thyagarajan, PARC, August 1993 79 * Modified by Bill Fenner, PARC, April 1994 80 * 81 * MROUTING Revision: 3.5.1.2 82 */ 83 84 #include <sys/param.h> 85 #include <sys/malloc.h> 86 #include <sys/systm.h> 87 #include <sys/timeout.h> 88 #include <sys/mbuf.h> 89 #include <sys/socket.h> 90 #include <sys/socketvar.h> 91 #include <sys/protosw.h> 92 #include <sys/kernel.h> 93 #include <sys/ioctl.h> 94 #include <sys/syslog.h> 95 #include <sys/sysctl.h> 96 97 #include <net/if.h> 98 #include <net/if_var.h> 99 #include <net/route.h> 100 101 #include <netinet/in.h> 102 #include <netinet6/in6_var.h> 103 #include <netinet/ip.h> 104 #include <netinet/ip6.h> 105 #include <netinet/icmp6.h> 106 #include <netinet6/ip6_var.h> 107 #include <netinet6/ip6_mroute.h> 108 #include <netinet/in_pcb.h> 109 110 /* #define MCAST_DEBUG */ 111 112 #ifdef MCAST_DEBUG 113 int mcast6_debug = 1; 114 #define DPRINTF(fmt, args...) \ 115 do { \ 116 if (mcast6_debug) \ 117 printf("%s:%d " fmt "\n", \ 118 __func__, __LINE__, ## args); \ 119 } while (0) 120 #else 121 #define DPRINTF(fmt, args...) \ 122 do { } while (0) 123 #endif 124 125 int ip6_mdq(struct mbuf *, struct ifnet *, struct rtentry *); 126 void phyint_send6(struct ifnet *, struct ip6_hdr *, struct mbuf *); 127 128 /* 129 * Globals. All but ip6_mrouter, ip6_mrtproto and mrt6stat could be static, 130 * except for netstat or debugging purposes. 131 */ 132 struct socket *ip6_mrouter[RT_TABLEID_MAX + 1]; 133 struct rttimer_queue ip6_mrouterq; 134 int ip6_mrouter_ver = 0; 135 int ip6_mrtproto; /* for netstat only */ 136 struct mrt6stat mrt6stat; 137 138 #define NO_RTE_FOUND 0x1 139 #define RTE_FOUND 0x2 140 141 /* 142 * Macros to compute elapsed time efficiently 143 * Borrowed from Van Jacobson's scheduling code 144 */ 145 #define TV_DELTA(a, b, delta) do { \ 146 int xxs; \ 147 \ 148 delta = (a).tv_usec - (b).tv_usec; \ 149 if ((xxs = (a).tv_sec - (b).tv_sec)) { \ 150 switch (xxs) { \ 151 case 2: \ 152 delta += 1000000; \ 153 /* FALLTHROUGH */ \ 154 case 1: \ 155 delta += 1000000; \ 156 break; \ 157 default: \ 158 delta += (1000000 * xxs); \ 159 } \ 160 } \ 161 } while (0) 162 163 #define TV_LT(a, b) (((a).tv_usec < (b).tv_usec && \ 164 (a).tv_sec <= (b).tv_sec) || (a).tv_sec < (b).tv_sec) 165 166 int get_sg6_cnt(struct sioc_sg_req6 *, unsigned int); 167 int get_mif6_cnt(struct sioc_mif_req6 *, unsigned int); 168 int ip6_mrouter_init(struct socket *, int, int); 169 int add_m6if(struct socket *, struct mif6ctl *); 170 int del_m6if(struct socket *, mifi_t *); 171 int add_m6fc(struct socket *, struct mf6cctl *); 172 int del_m6fc(struct socket *, struct mf6cctl *); 173 struct ifnet *mrt6_iflookupbymif(mifi_t, unsigned int); 174 struct rtentry *mf6c_find(struct ifnet *, struct in6_addr *, 175 struct in6_addr *, unsigned int); 176 struct rtentry *mrt6_mcast_add(struct ifnet *, struct sockaddr *, 177 struct sockaddr *); 178 void mrt6_mcast_del(struct rtentry *, unsigned int); 179 180 /* 181 * Handle MRT setsockopt commands to modify the multicast routing tables. 182 */ 183 int 184 ip6_mrouter_set(int cmd, struct socket *so, struct mbuf *m) 185 { 186 struct inpcb *inp = sotoinpcb(so); 187 188 if (cmd != MRT6_INIT && so != ip6_mrouter[inp->inp_rtableid]) 189 return (EPERM); 190 191 switch (cmd) { 192 case MRT6_INIT: 193 if (m == NULL || m->m_len < sizeof(int)) 194 return (EINVAL); 195 return (ip6_mrouter_init(so, *mtod(m, int *), cmd)); 196 case MRT6_DONE: 197 return (ip6_mrouter_done(so)); 198 case MRT6_ADD_MIF: 199 if (m == NULL || m->m_len < sizeof(struct mif6ctl)) 200 return (EINVAL); 201 return (add_m6if(so, mtod(m, struct mif6ctl *))); 202 case MRT6_DEL_MIF: 203 if (m == NULL || m->m_len < sizeof(mifi_t)) 204 return (EINVAL); 205 return (del_m6if(so, mtod(m, mifi_t *))); 206 case MRT6_ADD_MFC: 207 if (m == NULL || m->m_len < sizeof(struct mf6cctl)) 208 return (EINVAL); 209 return (add_m6fc(so, mtod(m, struct mf6cctl *))); 210 case MRT6_DEL_MFC: 211 if (m == NULL || m->m_len < sizeof(struct mf6cctl)) 212 return (EINVAL); 213 return (del_m6fc(so, mtod(m, struct mf6cctl *))); 214 default: 215 return (EOPNOTSUPP); 216 } 217 } 218 219 /* 220 * Handle MRT getsockopt commands 221 */ 222 int 223 ip6_mrouter_get(int cmd, struct socket *so, struct mbuf *m) 224 { 225 struct inpcb *inp = sotoinpcb(so); 226 227 if (so != ip6_mrouter[inp->inp_rtableid]) 228 return (EPERM); 229 230 switch (cmd) { 231 default: 232 return EOPNOTSUPP; 233 } 234 } 235 236 /* 237 * Handle ioctl commands to obtain information from the cache 238 */ 239 int 240 mrt6_ioctl(struct socket *so, u_long cmd, caddr_t data) 241 { 242 struct inpcb *inp = sotoinpcb(so); 243 int error; 244 245 if (inp == NULL) 246 return (ENOTCONN); 247 248 KERNEL_LOCK(); 249 250 switch (cmd) { 251 case SIOCGETSGCNT_IN6: 252 NET_LOCK_SHARED(); 253 error = get_sg6_cnt((struct sioc_sg_req6 *)data, 254 inp->inp_rtableid); 255 NET_UNLOCK_SHARED(); 256 break; 257 case SIOCGETMIFCNT_IN6: 258 NET_LOCK_SHARED(); 259 error = get_mif6_cnt((struct sioc_mif_req6 *)data, 260 inp->inp_rtableid); 261 NET_UNLOCK_SHARED(); 262 break; 263 default: 264 error = ENOTTY; 265 break; 266 } 267 268 KERNEL_UNLOCK(); 269 return error; 270 } 271 272 /* 273 * returns the packet, byte, rpf-failure count for the source group provided 274 */ 275 int 276 get_sg6_cnt(struct sioc_sg_req6 *req, unsigned int rtableid) 277 { 278 struct rtentry *rt; 279 struct mf6c *mf6c; 280 281 rt = mf6c_find(NULL, &req->src.sin6_addr, &req->grp.sin6_addr, 282 rtableid); 283 if (rt == NULL) { 284 req->pktcnt = req->bytecnt = req->wrong_if = 0xffffffff; 285 return EADDRNOTAVAIL; 286 } 287 288 req->pktcnt = req->bytecnt = req->wrong_if = 0; 289 do { 290 mf6c = (struct mf6c *)rt->rt_llinfo; 291 if (mf6c == NULL) 292 continue; 293 294 req->pktcnt += mf6c->mf6c_pkt_cnt; 295 req->bytecnt += mf6c->mf6c_byte_cnt; 296 req->wrong_if += mf6c->mf6c_wrong_if; 297 } while ((rt = rtable_iterate(rt)) != NULL); 298 299 return 0; 300 } 301 302 /* 303 * returns the input and output packet and byte counts on the mif provided 304 */ 305 int 306 get_mif6_cnt(struct sioc_mif_req6 *req, unsigned int rtableid) 307 { 308 struct ifnet *ifp; 309 struct mif6 *m6; 310 311 if ((ifp = mrt6_iflookupbymif(req->mifi, rtableid)) == NULL) 312 return EINVAL; 313 314 m6 = (struct mif6 *)ifp->if_mcast6; 315 req->icount = m6->m6_pkt_in; 316 req->ocount = m6->m6_pkt_out; 317 req->ibytes = m6->m6_bytes_in; 318 req->obytes = m6->m6_bytes_out; 319 320 return 0; 321 } 322 323 int 324 mrt6_sysctl_mif(void *oldp, size_t *oldlenp) 325 { 326 struct ifnet *ifp; 327 caddr_t where = oldp; 328 size_t needed, given; 329 struct mif6 *mifp; 330 struct mif6info minfo; 331 332 given = *oldlenp; 333 needed = 0; 334 memset(&minfo, 0, sizeof minfo); 335 TAILQ_FOREACH(ifp, &ifnetlist, if_list) { 336 if ((mifp = (struct mif6 *)ifp->if_mcast6) == NULL) 337 continue; 338 339 minfo.m6_mifi = mifp->m6_mifi; 340 minfo.m6_flags = mifp->m6_flags; 341 minfo.m6_lcl_addr = mifp->m6_lcl_addr; 342 minfo.m6_ifindex = ifp->if_index; 343 minfo.m6_pkt_in = mifp->m6_pkt_in; 344 minfo.m6_pkt_out = mifp->m6_pkt_out; 345 minfo.m6_bytes_in = mifp->m6_bytes_in; 346 minfo.m6_bytes_out = mifp->m6_bytes_out; 347 minfo.m6_rate_limit = mifp->m6_rate_limit; 348 349 needed += sizeof(minfo); 350 if (where && needed <= given) { 351 int error; 352 353 error = copyout(&minfo, where, sizeof(minfo)); 354 if (error) 355 return (error); 356 where += sizeof(minfo); 357 } 358 } 359 if (where) { 360 *oldlenp = needed; 361 if (given < needed) 362 return (ENOMEM); 363 } else 364 *oldlenp = (11 * needed) / 10; 365 366 return (0); 367 } 368 369 struct mf6csysctlarg { 370 struct mf6cinfo *ms6a_minfos; 371 size_t ms6a_len; 372 size_t ms6a_needed; 373 }; 374 375 int 376 mrt6_rtwalk_mf6csysctl(struct rtentry *rt, void *arg, unsigned int rtableid) 377 { 378 struct mf6c *mf6c = (struct mf6c *)rt->rt_llinfo; 379 struct mf6csysctlarg *msa = arg; 380 struct ifnet *ifp; 381 struct mif6 *m6; 382 struct mf6cinfo *minfo; 383 int new = 0; 384 385 /* Skip entries being removed. */ 386 if (mf6c == NULL) 387 return 0; 388 389 /* Skip non-multicast routes. */ 390 if (ISSET(rt->rt_flags, RTF_HOST | RTF_MULTICAST) != 391 (RTF_HOST | RTF_MULTICAST)) 392 return 0; 393 394 /* User just asked for the output size. */ 395 if (msa->ms6a_minfos == NULL) { 396 msa->ms6a_needed += sizeof(*minfo); 397 return 0; 398 } 399 400 /* Skip route with invalid interfaces. */ 401 if ((ifp = if_get(rt->rt_ifidx)) == NULL) 402 return 0; 403 if ((m6 = (struct mif6 *)ifp->if_mcast6) == NULL) { 404 if_put(ifp); 405 return 0; 406 } 407 408 for (minfo = msa->ms6a_minfos; 409 (uint8_t *)minfo < ((uint8_t *)msa->ms6a_minfos + msa->ms6a_len); 410 minfo++) { 411 /* Find a new entry or update old entry. */ 412 if (!IN6_ARE_ADDR_EQUAL(&minfo->mf6c_origin.sin6_addr, 413 &satosin6(rt->rt_gateway)->sin6_addr) || 414 !IN6_ARE_ADDR_EQUAL(&minfo->mf6c_mcastgrp.sin6_addr, 415 &satosin6(rt_key(rt))->sin6_addr)) { 416 if (!IN6_IS_ADDR_UNSPECIFIED( 417 &minfo->mf6c_origin.sin6_addr) || 418 !IN6_IS_ADDR_UNSPECIFIED( 419 &minfo->mf6c_mcastgrp.sin6_addr)) 420 continue; 421 422 new = 1; 423 } 424 425 minfo->mf6c_origin = *satosin6(rt->rt_gateway); 426 minfo->mf6c_mcastgrp = *satosin6(rt_key(rt)); 427 minfo->mf6c_parent = mf6c->mf6c_parent; 428 minfo->mf6c_pkt_cnt += mf6c->mf6c_pkt_cnt; 429 minfo->mf6c_byte_cnt += mf6c->mf6c_byte_cnt; 430 IF_SET(m6->m6_mifi, &minfo->mf6c_ifset); 431 break; 432 } 433 434 if (new != 0) 435 msa->ms6a_needed += sizeof(*minfo); 436 437 if_put(ifp); 438 439 return 0; 440 } 441 442 int 443 mrt6_sysctl_mfc(void *oldp, size_t *oldlenp) 444 { 445 unsigned int rtableid; 446 int error; 447 struct mf6csysctlarg msa; 448 449 if (oldp != NULL && *oldlenp > MAXPHYS) 450 return EINVAL; 451 452 if (oldp != NULL) 453 msa.ms6a_minfos = malloc(*oldlenp, M_TEMP, M_WAITOK | M_ZERO); 454 else 455 msa.ms6a_minfos = NULL; 456 457 msa.ms6a_len = *oldlenp; 458 msa.ms6a_needed = 0; 459 460 for (rtableid = 0; rtableid <= RT_TABLEID_MAX; rtableid++) { 461 rtable_walk(rtableid, AF_INET6, NULL, mrt6_rtwalk_mf6csysctl, 462 &msa); 463 } 464 465 if (msa.ms6a_minfos != NULL && msa.ms6a_needed > 0 && 466 (error = copyout(msa.ms6a_minfos, oldp, msa.ms6a_needed)) != 0) { 467 free(msa.ms6a_minfos, M_TEMP, *oldlenp); 468 return error; 469 } 470 471 free(msa.ms6a_minfos, M_TEMP, *oldlenp); 472 *oldlenp = msa.ms6a_needed; 473 474 return 0; 475 } 476 477 /* 478 * Enable multicast routing 479 */ 480 int 481 ip6_mrouter_init(struct socket *so, int v, int cmd) 482 { 483 struct inpcb *inp = sotoinpcb(so); 484 unsigned int rtableid = inp->inp_rtableid; 485 486 if (so->so_type != SOCK_RAW || 487 so->so_proto->pr_protocol != IPPROTO_ICMPV6) 488 return (EOPNOTSUPP); 489 490 if (v != 1) 491 return (ENOPROTOOPT); 492 493 if (ip6_mrouter[rtableid] != NULL) 494 return (EADDRINUSE); 495 496 ip6_mrouter[rtableid] = so; 497 ip6_mrouter_ver = cmd; 498 499 return (0); 500 } 501 502 int 503 mrouter6_rtwalk_delete(struct rtentry *rt, void *arg, unsigned int rtableid) 504 { 505 /* Skip non-multicast routes. */ 506 if (ISSET(rt->rt_flags, RTF_HOST | RTF_MULTICAST) != 507 (RTF_HOST | RTF_MULTICAST)) 508 return 0; 509 510 return EEXIST; 511 } 512 513 /* 514 * Disable multicast routing 515 */ 516 int 517 ip6_mrouter_done(struct socket *so) 518 { 519 struct inpcb *inp = sotoinpcb(so); 520 struct ifnet *ifp; 521 unsigned int rtableid = inp->inp_rtableid; 522 int error; 523 524 NET_ASSERT_LOCKED(); 525 526 /* Delete all remaining installed multicast routes. */ 527 do { 528 struct rtentry *rt = NULL; 529 530 error = rtable_walk(rtableid, AF_INET6, &rt, 531 mrouter6_rtwalk_delete, NULL); 532 if (rt != NULL && error == EEXIST) { 533 mrt6_mcast_del(rt, rtableid); 534 error = EAGAIN; 535 } 536 rtfree(rt); 537 } while (error == EAGAIN); 538 539 /* Unregister all interfaces in the domain. */ 540 TAILQ_FOREACH(ifp, &ifnetlist, if_list) { 541 if (ifp->if_rdomain != rtableid) 542 continue; 543 544 ip6_mrouter_detach(ifp); 545 } 546 547 ip6_mrouter[inp->inp_rtableid] = NULL; 548 ip6_mrouter_ver = 0; 549 550 return 0; 551 } 552 553 void 554 ip6_mrouter_detach(struct ifnet *ifp) 555 { 556 struct mif6 *m6 = (struct mif6 *)ifp->if_mcast6; 557 struct in6_ifreq ifr; 558 559 if (m6 == NULL) 560 return; 561 562 ifp->if_mcast6 = NULL; 563 564 memset(&ifr, 0, sizeof(ifr)); 565 ifr.ifr_addr.sin6_family = AF_INET6; 566 ifr.ifr_addr.sin6_addr = in6addr_any; 567 KERNEL_LOCK(); 568 (*ifp->if_ioctl)(ifp, SIOCDELMULTI, (caddr_t)&ifr); 569 KERNEL_UNLOCK(); 570 571 free(m6, M_MRTABLE, sizeof(*m6)); 572 } 573 574 /* 575 * Add a mif to the mif table 576 */ 577 int 578 add_m6if(struct socket *so, struct mif6ctl *mifcp) 579 { 580 struct inpcb *inp = sotoinpcb(so); 581 struct mif6 *mifp; 582 struct ifnet *ifp; 583 struct in6_ifreq ifr; 584 int error; 585 unsigned int rtableid = inp->inp_rtableid; 586 587 NET_ASSERT_LOCKED(); 588 589 if (mifcp->mif6c_mifi >= MAXMIFS) 590 return EINVAL; 591 592 if (mrt6_iflookupbymif(mifcp->mif6c_mifi, rtableid) != NULL) 593 return EADDRINUSE; /* XXX: is it appropriate? */ 594 595 { 596 ifp = if_get(mifcp->mif6c_pifi); 597 if (ifp == NULL) 598 return ENXIO; 599 600 /* Make sure the interface supports multicast */ 601 if ((ifp->if_flags & IFF_MULTICAST) == 0) { 602 if_put(ifp); 603 return EOPNOTSUPP; 604 } 605 606 /* 607 * Enable promiscuous reception of all IPv6 multicasts 608 * from the interface. 609 */ 610 memset(&ifr, 0, sizeof(ifr)); 611 ifr.ifr_addr.sin6_family = AF_INET6; 612 ifr.ifr_addr.sin6_addr = in6addr_any; 613 error = (*ifp->if_ioctl)(ifp, SIOCADDMULTI, (caddr_t)&ifr); 614 615 if (error) { 616 if_put(ifp); 617 return error; 618 } 619 } 620 621 mifp = malloc(sizeof(*mifp), M_MRTABLE, M_WAITOK | M_ZERO); 622 ifp->if_mcast6 = (caddr_t)mifp; 623 mifp->m6_mifi = mifcp->mif6c_mifi; 624 mifp->m6_flags = mifcp->mif6c_flags; 625 #ifdef notyet 626 /* scaling up here allows division by 1024 in critical code */ 627 mifp->m6_rate_limit = mifcp->mif6c_rate_limit * 1024 / 1000; 628 #endif 629 630 if_put(ifp); 631 632 return 0; 633 } 634 635 /* 636 * Delete a mif from the mif table 637 */ 638 int 639 del_m6if(struct socket *so, mifi_t *mifip) 640 { 641 struct inpcb *inp = sotoinpcb(so); 642 struct ifnet *ifp; 643 644 NET_ASSERT_LOCKED(); 645 646 if (*mifip >= MAXMIFS) 647 return EINVAL; 648 if ((ifp = mrt6_iflookupbymif(*mifip, inp->inp_rtableid)) == NULL) 649 return EINVAL; 650 651 ip6_mrouter_detach(ifp); 652 653 return 0; 654 } 655 656 int 657 mf6c_add_route(struct ifnet *ifp, struct sockaddr *origin, 658 struct sockaddr *group, struct mf6cctl *mf6cc, int wait) 659 { 660 struct rtentry *rt; 661 struct mf6c *mf6c; 662 unsigned int rtableid = ifp->if_rdomain; 663 #ifdef MCAST_DEBUG 664 char bsrc[INET6_ADDRSTRLEN], bdst[INET6_ADDRSTRLEN]; 665 #endif /* MCAST_DEBUG */ 666 667 rt = mrt6_mcast_add(ifp, origin, group); 668 if (rt == NULL) 669 return ENOENT; 670 671 mf6c = malloc(sizeof(*mf6c), M_MRTABLE, wait | M_ZERO); 672 if (mf6c == NULL) { 673 DPRINTF("origin %s group %s parent %d (%s) malloc failed", 674 inet_ntop(AF_INET6, origin, bsrc, sizeof(bsrc)), 675 inet_ntop(AF_INET6, group, bdst, sizeof(bdst)), 676 mf6cc->mf6cc_parent, ifp->if_xname); 677 mrt6_mcast_del(rt, rtableid); 678 rtfree(rt); 679 return ENOMEM; 680 } 681 682 rt->rt_llinfo = (caddr_t)mf6c; 683 rt_timer_add(rt, &ip6_mrouterq, rtableid); 684 mf6c->mf6c_parent = mf6cc->mf6cc_parent; 685 rtfree(rt); 686 687 return 0; 688 } 689 690 void 691 mf6c_update(struct mf6cctl *mf6cc, int wait, unsigned int rtableid) 692 { 693 struct rtentry *rt; 694 struct mf6c *mf6c; 695 struct ifnet *ifp; 696 struct sockaddr_in6 osin6, gsin6; 697 mifi_t mifi; 698 #ifdef MCAST_DEBUG 699 char bdst[INET6_ADDRSTRLEN]; 700 #endif /* MCAST_DEBUG */ 701 702 memset(&osin6, 0, sizeof(osin6)); 703 osin6.sin6_family = AF_INET6; 704 osin6.sin6_len = sizeof(osin6); 705 osin6.sin6_addr = mf6cc->mf6cc_origin.sin6_addr; 706 707 memset(&gsin6, 0, sizeof(gsin6)); 708 gsin6.sin6_family = AF_INET6; 709 gsin6.sin6_len = sizeof(gsin6); 710 gsin6.sin6_addr = mf6cc->mf6cc_mcastgrp.sin6_addr; 711 712 for (mifi = 0; mifi < MAXMIFS; mifi++) { 713 if (mifi == mf6cc->mf6cc_parent) 714 continue; 715 716 /* Test for mif existence and then update the entry. */ 717 if ((ifp = mrt6_iflookupbymif(mifi, rtableid)) == NULL) 718 continue; 719 720 rt = mf6c_find(ifp, &mf6cc->mf6cc_origin.sin6_addr, 721 &mf6cc->mf6cc_mcastgrp.sin6_addr, rtableid); 722 723 /* mif not configured or removed. */ 724 if (!IF_ISSET(mifi, &mf6cc->mf6cc_ifset)) { 725 /* Route doesn't exist, nothing to do. */ 726 if (rt == NULL) 727 continue; 728 729 DPRINTF("del route (group %s) for mif %d (%s)", 730 inet_ntop(AF_INET6, 731 &mf6cc->mf6cc_mcastgrp.sin6_addr, bdst, 732 sizeof(bdst)), mifi, ifp->if_xname); 733 mrt6_mcast_del(rt, rtableid); 734 rtfree(rt); 735 continue; 736 } 737 738 /* Route exists, look for changes. */ 739 if (rt != NULL) { 740 mf6c = (struct mf6c *)rt->rt_llinfo; 741 /* Skip route being deleted. */ 742 if (mf6c == NULL) { 743 rtfree(rt); 744 continue; 745 } 746 747 /* No new changes to apply. */ 748 if (mf6cc->mf6cc_parent == mf6c->mf6c_parent) { 749 rtfree(rt); 750 continue; 751 } 752 753 DPRINTF("update route (group %s) for mif %d (%s)", 754 inet_ntop(AF_INET6, 755 &mf6cc->mf6cc_mcastgrp.sin6_addr, bdst, 756 sizeof(bdst)), mifi, ifp->if_xname); 757 758 mf6c->mf6c_parent = mf6cc->mf6cc_parent; 759 rtfree(rt); 760 continue; 761 } 762 763 DPRINTF("add route (group %s) for mif %d (%s)", 764 inet_ntop(AF_INET6, &mf6cc->mf6cc_mcastgrp.sin6_addr, 765 bdst, sizeof(bdst)), mifi, ifp->if_xname); 766 767 mf6c_add_route(ifp, sin6tosa(&osin6), sin6tosa(&gsin6), 768 mf6cc, wait); 769 } 770 771 /* Create route for the parent interface. */ 772 if ((ifp = mrt6_iflookupbymif(mf6cc->mf6cc_parent, 773 rtableid)) == NULL) { 774 DPRINTF("failed to find upstream interface %d", 775 mf6cc->mf6cc_parent); 776 return; 777 } 778 779 /* We already have a route, nothing to do here. */ 780 if ((rt = mf6c_find(ifp, &mf6cc->mf6cc_origin.sin6_addr, 781 &mf6cc->mf6cc_mcastgrp.sin6_addr, rtableid)) != NULL) { 782 rtfree(rt); 783 return; 784 } 785 786 DPRINTF("add upstream route (group %s) for if %s", 787 inet_ntop(AF_INET6, &mf6cc->mf6cc_mcastgrp.sin6_addr, 788 bdst, sizeof(bdst)), ifp->if_xname); 789 mf6c_add_route(ifp, sin6tosa(&osin6), sin6tosa(&gsin6), mf6cc, wait); 790 } 791 792 int 793 mf6c_add(struct mf6cctl *mfccp, struct in6_addr *origin, 794 struct in6_addr *group, int vidx, unsigned int rtableid, int wait) 795 { 796 struct ifnet *ifp; 797 struct mif6 *m6; 798 struct mf6cctl mf6cc; 799 800 ifp = mrt6_iflookupbymif(vidx, rtableid); 801 if (ifp == NULL || 802 (m6 = (struct mif6 *)ifp->if_mcast6) == NULL) 803 return ENOENT; 804 805 memset(&mf6cc, 0, sizeof(mf6cc)); 806 if (mfccp == NULL) { 807 mf6cc.mf6cc_origin.sin6_family = AF_INET6; 808 mf6cc.mf6cc_origin.sin6_len = sizeof(mf6cc.mf6cc_origin); 809 mf6cc.mf6cc_origin.sin6_addr = *origin; 810 mf6cc.mf6cc_mcastgrp.sin6_family = AF_INET6; 811 mf6cc.mf6cc_mcastgrp.sin6_len = sizeof(mf6cc.mf6cc_mcastgrp); 812 mf6cc.mf6cc_mcastgrp.sin6_addr = *group; 813 mf6cc.mf6cc_parent = vidx; 814 } else 815 memcpy(&mf6cc, mfccp, sizeof(mf6cc)); 816 817 mf6c_update(&mf6cc, wait, rtableid); 818 819 return 0; 820 } 821 822 int 823 add_m6fc(struct socket *so, struct mf6cctl *mfccp) 824 { 825 struct inpcb *inp = sotoinpcb(so); 826 unsigned int rtableid = inp->inp_rtableid; 827 828 NET_ASSERT_LOCKED(); 829 830 return mf6c_add(mfccp, &mfccp->mf6cc_origin.sin6_addr, 831 &mfccp->mf6cc_mcastgrp.sin6_addr, mfccp->mf6cc_parent, 832 rtableid, M_WAITOK); 833 } 834 835 int 836 del_m6fc(struct socket *so, struct mf6cctl *mfccp) 837 { 838 struct inpcb *inp = sotoinpcb(so); 839 struct rtentry *rt; 840 unsigned int rtableid = inp->inp_rtableid; 841 842 NET_ASSERT_LOCKED(); 843 844 while ((rt = mf6c_find(NULL, &mfccp->mf6cc_origin.sin6_addr, 845 &mfccp->mf6cc_mcastgrp.sin6_addr, rtableid)) != NULL) { 846 mrt6_mcast_del(rt, rtableid); 847 rtfree(rt); 848 } 849 850 return 0; 851 } 852 853 int 854 socket6_send(struct socket *s, struct mbuf *mm, struct sockaddr_in6 *src) 855 { 856 if (s) { 857 if (sbappendaddr(s, &s->so_rcv, sin6tosa(src), mm, NULL) != 0) { 858 sorwakeup(s); 859 return 0; 860 } 861 } 862 m_freem(mm); 863 return -1; 864 } 865 866 /* 867 * IPv6 multicast forwarding function. This function assumes that the packet 868 * pointed to by "ip6" has arrived on (or is about to be sent to) the interface 869 * pointed to by "ifp", and the packet is to be relayed to other networks 870 * that have members of the packet's destination IPv6 multicast group. 871 * 872 * The packet is returned unscathed to the caller, unless it is 873 * erroneous, in which case a non-zero return value tells the caller to 874 * discard it. 875 */ 876 int 877 ip6_mforward(struct ip6_hdr *ip6, struct ifnet *ifp, struct mbuf *m) 878 { 879 struct rtentry *rt; 880 struct mif6 *mifp; 881 struct mbuf *mm; 882 struct sockaddr_in6 sin6; 883 unsigned int rtableid = ifp->if_rdomain; 884 885 NET_ASSERT_LOCKED(); 886 887 /* 888 * Don't forward a packet with Hop limit of zero or one, 889 * or a packet destined to a local-only group. 890 */ 891 if (ip6->ip6_hlim <= 1 || IN6_IS_ADDR_MC_INTFACELOCAL(&ip6->ip6_dst) || 892 IN6_IS_ADDR_MC_LINKLOCAL(&ip6->ip6_dst)) 893 return 0; 894 ip6->ip6_hlim--; 895 896 /* 897 * Source address check: do not forward packets with unspecified 898 * source. It was discussed in July 2000, on ipngwg mailing list. 899 * This is rather more serious than unicast cases, because some 900 * MLD packets can be sent with the unspecified source address 901 * (although such packets must normally set 1 to the hop limit field). 902 */ 903 if (IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_src)) { 904 ip6stat_inc(ip6s_cantforward); 905 if (ip6_log_time + ip6_log_interval < getuptime()) { 906 char src[INET6_ADDRSTRLEN], dst[INET6_ADDRSTRLEN]; 907 908 ip6_log_time = getuptime(); 909 910 inet_ntop(AF_INET6, &ip6->ip6_src, src, sizeof(src)); 911 inet_ntop(AF_INET6, &ip6->ip6_dst, dst, sizeof(dst)); 912 log(LOG_DEBUG, "cannot forward " 913 "from %s to %s nxt %d received on interface %u\n", 914 src, dst, ip6->ip6_nxt, m->m_pkthdr.ph_ifidx); 915 } 916 return 0; 917 } 918 919 /* 920 * Determine forwarding mifs from the forwarding cache table 921 */ 922 rt = mf6c_find(NULL, &ip6->ip6_src, &ip6->ip6_dst, rtableid); 923 924 /* Entry exists, so forward if necessary */ 925 if (rt) { 926 return (ip6_mdq(m, ifp, rt)); 927 } else { 928 /* 929 * If we don't have a route for packet's origin, 930 * Make a copy of the packet & 931 * send message to routing daemon 932 */ 933 934 mrt6stat.mrt6s_no_route++; 935 936 { 937 struct mrt6msg *im; 938 939 if ((mifp = (struct mif6 *)ifp->if_mcast6) == NULL) 940 return EHOSTUNREACH; 941 942 /* 943 * Make a copy of the header to send to the user 944 * level process 945 */ 946 mm = m_copym(m, 0, sizeof(struct ip6_hdr), M_NOWAIT); 947 if (mm == NULL) 948 return ENOBUFS; 949 950 /* 951 * Send message to routing daemon 952 */ 953 (void)memset(&sin6, 0, sizeof(sin6)); 954 sin6.sin6_len = sizeof(sin6); 955 sin6.sin6_family = AF_INET6; 956 sin6.sin6_addr = ip6->ip6_src; 957 958 im = NULL; 959 switch (ip6_mrouter_ver) { 960 case MRT6_INIT: 961 im = mtod(mm, struct mrt6msg *); 962 im->im6_msgtype = MRT6MSG_NOCACHE; 963 im->im6_mbz = 0; 964 im->im6_mif = mifp->m6_mifi; 965 break; 966 default: 967 m_freem(mm); 968 return EINVAL; 969 } 970 971 if (socket6_send(ip6_mrouter[rtableid], mm, 972 &sin6) < 0) { 973 log(LOG_WARNING, "ip6_mforward: ip6_mrouter " 974 "socket queue full\n"); 975 mrt6stat.mrt6s_upq_sockfull++; 976 return ENOBUFS; 977 } 978 979 mrt6stat.mrt6s_upcalls++; 980 981 mf6c_add(NULL, &ip6->ip6_src, &ip6->ip6_dst, 982 mifp->m6_mifi, rtableid, M_NOWAIT); 983 } 984 985 return 0; 986 } 987 } 988 989 void 990 mf6c_expire_route(struct rtentry *rt, u_int rtableid) 991 { 992 struct mf6c *mf6c = (struct mf6c *)rt->rt_llinfo; 993 #ifdef MCAST_DEBUG 994 char bsrc[INET6_ADDRSTRLEN], bdst[INET6_ADDRSTRLEN]; 995 #endif /* MCAST_DEBUG */ 996 997 /* Skip entry being deleted. */ 998 if (mf6c == NULL) 999 return; 1000 1001 DPRINTF("origin %s group %s interface %d expire %s", 1002 inet_ntop(AF_INET6, &satosin6(rt->rt_gateway)->sin6_addr, 1003 bsrc, sizeof(bsrc)), 1004 inet_ntop(AF_INET6, &satosin6(rt_key(rt))->sin6_addr, 1005 bdst, sizeof(bdst)), rt->rt_ifidx, 1006 mf6c->mf6c_expire ? "yes" : "no"); 1007 1008 if (mf6c->mf6c_expire == 0) { 1009 mf6c->mf6c_expire = 1; 1010 rt_timer_add(rt, &ip6_mrouterq, rtableid); 1011 return; 1012 } 1013 1014 mrt6_mcast_del(rt, rtableid); 1015 } 1016 1017 /* 1018 * Packet forwarding routine once entry in the cache is made 1019 */ 1020 int 1021 ip6_mdq(struct mbuf *m, struct ifnet *ifp, struct rtentry *rt) 1022 { 1023 struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); 1024 struct mif6 *m6, *mifp = (struct mif6 *)ifp->if_mcast6; 1025 struct mf6c *mf6c = (struct mf6c *)rt->rt_llinfo; 1026 struct ifnet *ifn; 1027 int plen = m->m_pkthdr.len; 1028 1029 if (mifp == NULL || mf6c == NULL) { 1030 rtfree(rt); 1031 return EHOSTUNREACH; 1032 } 1033 1034 /* 1035 * Don't forward if it didn't arrive from the parent mif 1036 * for its origin. 1037 */ 1038 if (mifp->m6_mifi != mf6c->mf6c_parent) { 1039 /* came in the wrong interface */ 1040 mrt6stat.mrt6s_wrong_if++; 1041 mf6c->mf6c_wrong_if++; 1042 rtfree(rt); 1043 return 0; 1044 } /* if wrong iif */ 1045 1046 /* If I sourced this packet, it counts as output, else it was input. */ 1047 if (m->m_pkthdr.ph_ifidx == 0) { 1048 /* XXX: is ph_ifidx really 0 when output?? */ 1049 mifp->m6_pkt_out++; 1050 mifp->m6_bytes_out += plen; 1051 } else { 1052 mifp->m6_pkt_in++; 1053 mifp->m6_bytes_in += plen; 1054 } 1055 1056 /* 1057 * For each mif, forward a copy of the packet if there are group 1058 * members downstream on the interface. 1059 */ 1060 do { 1061 /* Don't consider non multicast routes. */ 1062 if (ISSET(rt->rt_flags, RTF_HOST | RTF_MULTICAST) != 1063 (RTF_HOST | RTF_MULTICAST)) 1064 continue; 1065 1066 mf6c = (struct mf6c *)rt->rt_llinfo; 1067 if (mf6c == NULL) 1068 continue; 1069 1070 mf6c->mf6c_pkt_cnt++; 1071 mf6c->mf6c_byte_cnt += m->m_pkthdr.len; 1072 1073 /* Don't let this route expire. */ 1074 mf6c->mf6c_expire = 0; 1075 1076 if ((ifn = if_get(rt->rt_ifidx)) == NULL) 1077 continue; 1078 1079 /* Sanity check: did we configure this? */ 1080 if ((m6 = (struct mif6 *)ifn->if_mcast6) == NULL) { 1081 if_put(ifn); 1082 continue; 1083 } 1084 1085 /* Don't send in the upstream interface. */ 1086 if (mf6c->mf6c_parent == m6->m6_mifi) { 1087 if_put(ifn); 1088 continue; 1089 } 1090 1091 /* 1092 * check if the outgoing packet is going to break 1093 * a scope boundary. 1094 */ 1095 if ((mifp->m6_flags & MIFF_REGISTER) == 0 && 1096 (m6->m6_flags & MIFF_REGISTER) == 0 && 1097 (in6_addr2scopeid(ifp->if_index, &ip6->ip6_dst) != 1098 in6_addr2scopeid(ifn->if_index, &ip6->ip6_dst) || 1099 in6_addr2scopeid(ifp->if_index, &ip6->ip6_src) != 1100 in6_addr2scopeid(ifn->if_index, &ip6->ip6_src))) { 1101 if_put(ifn); 1102 ip6stat_inc(ip6s_badscope); 1103 continue; 1104 } 1105 1106 m6->m6_pkt_out++; 1107 m6->m6_bytes_out += plen; 1108 1109 phyint_send6(ifn, ip6, m); 1110 if_put(ifn); 1111 } while ((rt = rtable_iterate(rt)) != NULL); 1112 1113 return 0; 1114 } 1115 1116 void 1117 phyint_send6(struct ifnet *ifp, struct ip6_hdr *ip6, struct mbuf *m) 1118 { 1119 struct mbuf *mb_copy; 1120 struct sockaddr_in6 *dst6, sin6; 1121 int error = 0; 1122 1123 NET_ASSERT_LOCKED(); 1124 1125 /* 1126 * Make a new reference to the packet; make sure that 1127 * the IPv6 header is actually copied, not just referenced, 1128 * so that ip6_output() only scribbles on the copy. 1129 */ 1130 mb_copy = m_dup_pkt(m, max_linkhdr, M_NOWAIT); 1131 if (mb_copy == NULL) 1132 return; 1133 /* set MCAST flag to the outgoing packet */ 1134 mb_copy->m_flags |= M_MCAST; 1135 1136 /* 1137 * If we sourced the packet, call ip6_output since we may divide 1138 * the packet into fragments when the packet is too big for the 1139 * outgoing interface. 1140 * Otherwise, we can simply send the packet to the interface 1141 * sending queue. 1142 */ 1143 if (m->m_pkthdr.ph_ifidx == 0) { 1144 struct ip6_moptions im6o; 1145 1146 im6o.im6o_ifidx = ifp->if_index; 1147 /* XXX: ip6_output will override ip6->ip6_hlim */ 1148 im6o.im6o_hlim = ip6->ip6_hlim; 1149 im6o.im6o_loop = 1; 1150 error = ip6_output(mb_copy, NULL, NULL, IPV6_FORWARDING, &im6o, 1151 NULL); 1152 return; 1153 } 1154 1155 /* 1156 * If we belong to the destination multicast group 1157 * on the outgoing interface, loop back a copy. 1158 */ 1159 dst6 = &sin6; 1160 memset(&sin6, 0, sizeof(sin6)); 1161 if (in6_hasmulti(&ip6->ip6_dst, ifp)) { 1162 dst6->sin6_len = sizeof(struct sockaddr_in6); 1163 dst6->sin6_family = AF_INET6; 1164 dst6->sin6_addr = ip6->ip6_dst; 1165 ip6_mloopback(ifp, m, dst6); 1166 } 1167 /* 1168 * Put the packet into the sending queue of the outgoing interface 1169 * if it would fit in the MTU of the interface. 1170 */ 1171 if (mb_copy->m_pkthdr.len <= ifp->if_mtu || ifp->if_mtu < IPV6_MMTU) { 1172 dst6->sin6_len = sizeof(struct sockaddr_in6); 1173 dst6->sin6_family = AF_INET6; 1174 dst6->sin6_addr = ip6->ip6_dst; 1175 error = ifp->if_output(ifp, mb_copy, sin6tosa(dst6), NULL); 1176 } else { 1177 if (ip6_mcast_pmtu) 1178 icmp6_error(mb_copy, ICMP6_PACKET_TOO_BIG, 0, 1179 ifp->if_mtu); 1180 else { 1181 m_freem(mb_copy); /* simply discard the packet */ 1182 } 1183 } 1184 } 1185 1186 struct ifnet * 1187 mrt6_iflookupbymif(mifi_t mifi, unsigned int rtableid) 1188 { 1189 struct mif6 *m6; 1190 struct ifnet *ifp; 1191 1192 TAILQ_FOREACH(ifp, &ifnetlist, if_list) { 1193 if (ifp->if_rdomain != rtableid) 1194 continue; 1195 if ((m6 = (struct mif6 *)ifp->if_mcast6) == NULL) 1196 continue; 1197 if (m6->m6_mifi != mifi) 1198 continue; 1199 1200 return ifp; 1201 } 1202 1203 return NULL; 1204 } 1205 1206 struct rtentry * 1207 mf6c_find(struct ifnet *ifp, struct in6_addr *origin, struct in6_addr *group, 1208 unsigned int rtableid) 1209 { 1210 struct rtentry *rt; 1211 struct sockaddr_in6 msin6; 1212 1213 memset(&msin6, 0, sizeof(msin6)); 1214 msin6.sin6_family = AF_INET6; 1215 msin6.sin6_len = sizeof(msin6); 1216 msin6.sin6_addr = *group; 1217 1218 rt = rtalloc(sin6tosa(&msin6), 0, rtableid); 1219 do { 1220 if (!rtisvalid(rt)) { 1221 rtfree(rt); 1222 return NULL; 1223 } 1224 if (ISSET(rt->rt_flags, RTF_HOST | RTF_MULTICAST) != 1225 (RTF_HOST | RTF_MULTICAST)) 1226 continue; 1227 /* Return first occurrence if interface is not specified. */ 1228 if (ifp == NULL) 1229 return rt; 1230 if (rt->rt_ifidx == ifp->if_index) 1231 return rt; 1232 } while ((rt = rtable_iterate(rt)) != NULL); 1233 1234 return NULL; 1235 } 1236 1237 struct rtentry * 1238 mrt6_mcast_add(struct ifnet *ifp, struct sockaddr *origin, 1239 struct sockaddr *group) 1240 { 1241 struct ifaddr *ifa; 1242 int rv; 1243 unsigned int rtableid = ifp->if_rdomain; 1244 1245 TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) { 1246 if (ifa->ifa_addr->sa_family == AF_INET6) 1247 break; 1248 } 1249 if (ifa == NULL) { 1250 DPRINTF("ifa == NULL"); 1251 return NULL; 1252 } 1253 1254 rv = rt_ifa_add(ifa, RTF_HOST | RTF_MULTICAST | RTF_MPATH, group, 1255 ifp->if_rdomain); 1256 if (rv != 0) { 1257 DPRINTF("rt_ifa_add failed %d", rv); 1258 return NULL; 1259 } 1260 1261 return mf6c_find(ifp, NULL, &satosin6(group)->sin6_addr, rtableid); 1262 } 1263 1264 void 1265 mrt6_mcast_del(struct rtentry *rt, unsigned int rtableid) 1266 { 1267 struct ifnet *ifp; 1268 int error; 1269 1270 /* Remove all timers related to this route. */ 1271 rt_timer_remove_all(rt); 1272 1273 free(rt->rt_llinfo, M_MRTABLE, sizeof(struct mf6c)); 1274 rt->rt_llinfo = NULL; 1275 1276 ifp = if_get(rt->rt_ifidx); 1277 if (ifp == NULL) 1278 return; 1279 error = rtdeletemsg(rt, ifp, rtableid); 1280 if_put(ifp); 1281 1282 if (error) 1283 DPRINTF("delete route error %d\n", error); 1284 } 1285