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