diff options
-rw-r--r-- | pimd/pim6_mld.c | 239 | ||||
-rw-r--r-- | pimd/pim6_mld.h | 5 | ||||
-rw-r--r-- | pimd/pim_instance.c | 2 | ||||
-rw-r--r-- | pimd/pim_instance.h | 6 |
4 files changed, 149 insertions, 103 deletions
diff --git a/pimd/pim6_mld.c b/pimd/pim6_mld.c index 7209989cb..a67d33ca9 100644 --- a/pimd/pim6_mld.c +++ b/pimd/pim6_mld.c @@ -1591,7 +1591,7 @@ static bool ip6_check_hopopts_ra(uint8_t *hopopts, size_t hopopt_len, static void gm_t_recv(struct thread *t) { - struct gm_if *gm_ifp = THREAD_ARG(t); + struct pim_instance *pim = THREAD_ARG(t); union { char buf[CMSG_SPACE(sizeof(struct in6_pktinfo)) + CMSG_SPACE(256) /* hop options */ + @@ -1610,8 +1610,8 @@ static void gm_t_recv(struct thread *t) ssize_t nread; size_t pktlen; - thread_add_read(router->master, gm_t_recv, gm_ifp, gm_ifp->sock, - &gm_ifp->t_recv); + thread_add_read(router->master, gm_t_recv, pim, pim->gm_socket, + &pim->t_gm_recv); iov->iov_base = rxbuf; iov->iov_len = sizeof(rxbuf); @@ -1624,10 +1624,10 @@ static void gm_t_recv(struct thread *t) mh->msg_iovlen = array_size(iov); mh->msg_flags = 0; - nread = recvmsg(gm_ifp->sock, mh, MSG_PEEK | MSG_TRUNC); + nread = recvmsg(pim->gm_socket, mh, MSG_PEEK | MSG_TRUNC); if (nread <= 0) { - zlog_err(log_ifp("RX error: %m")); - gm_ifp->stats.rx_drop_sys++; + zlog_err("(VRF %s) RX error: %m", pim->vrf->name); + pim->gm_rx_drop_sys++; return; } @@ -1635,14 +1635,23 @@ static void gm_t_recv(struct thread *t) iov->iov_base = XMALLOC(MTYPE_GM_PACKET, nread); iov->iov_len = nread; } - nread = recvmsg(gm_ifp->sock, mh, 0); + nread = recvmsg(pim->gm_socket, mh, 0); if (nread <= 0) { - zlog_err(log_ifp("RX error: %m")); - gm_ifp->stats.rx_drop_sys++; + zlog_err("(VRF %s) RX error: %m", pim->vrf->name); + pim->gm_rx_drop_sys++; goto out_free; } - if ((int)pkt_src->sin6_scope_id != gm_ifp->ifp->ifindex) + struct interface *ifp; + + ifp = if_lookup_by_index(pkt_src->sin6_scope_id, pim->vrf->vrf_id); + if (!ifp || !ifp->info) + goto out_free; + + struct pim_interface *pim_ifp = ifp->info; + struct gm_if *gm_ifp = pim_ifp->mld; + + if (!gm_ifp) goto out_free; for (cmsg = CMSG_FIRSTHDR(mh); cmsg; cmsg = CMSG_NXTHDR(mh, cmsg)) { @@ -1666,7 +1675,7 @@ static void gm_t_recv(struct thread *t) if (!pktinfo || !hoplimit) { zlog_err(log_ifp( "BUG: packet without IPV6_PKTINFO or IPV6_HOPLIMIT")); - gm_ifp->stats.rx_drop_sys++; + pim->gm_rx_drop_sys++; goto out_free; } @@ -1739,7 +1748,8 @@ static void gm_send_query(struct gm_if *gm_ifp, pim_addr grp, .next_hdr = IPPROTO_ICMPV6, }; union { - char buf[CMSG_SPACE(8)]; + char buf[CMSG_SPACE(8) /* hop options */ + + CMSG_SPACE(sizeof(struct in6_pktinfo))]; struct cmsghdr align; } cmsg = {}; struct cmsghdr *cmh; @@ -1748,6 +1758,7 @@ static void gm_send_query(struct gm_if *gm_ifp, pim_addr grp, size_t iov_len; ssize_t ret, expect_ret; uint8_t *dp; + struct in6_pktinfo *pktinfo; if (if_is_loopback(gm_ifp->ifp)) { /* Linux is a bit odd with multicast on loopback */ @@ -1799,6 +1810,7 @@ static void gm_send_query(struct gm_if *gm_ifp, pim_addr grp, mh->msg_iovlen = iov_len - 1; mh->msg_control = &cmsg; mh->msg_controllen = sizeof(cmsg.buf); + cmh = CMSG_FIRSTHDR(mh); cmh->cmsg_level = IPPROTO_IPV6; cmh->cmsg_type = IPV6_HOPOPTS; @@ -1813,12 +1825,20 @@ static void gm_send_query(struct gm_if *gm_ifp, pim_addr grp, *dp++ = 0; /* pad0 */ *dp++ = 0; /* pad0 */ + cmh = CMSG_NXTHDR(mh, cmh); + cmh->cmsg_level = IPPROTO_IPV6; + cmh->cmsg_type = IPV6_PKTINFO; + cmh->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); + pktinfo = (struct in6_pktinfo *)CMSG_DATA(cmh); + pktinfo->ipi6_ifindex = gm_ifp->ifp->ifindex; + pktinfo->ipi6_addr = gm_ifp->cur_ll_lowest; + expect_ret = iov[1].iov_len; if (iov_len == 3) expect_ret += iov[2].iov_len; frr_with_privs (&pimd_privs) { - ret = sendmsg(gm_ifp->sock, mh, 0); + ret = sendmsg(gm_ifp->pim->gm_socket, mh, 0); } if (ret != expect_ret) { @@ -1899,7 +1919,7 @@ static void gm_trigger_specific(struct gm_sg *sg) if (!IPV6_ADDR_SAME(&gm_ifp->querier, &pim_ifp->ll_lowest)) return; - if (gm_ifp->sock == -1) + if (gm_ifp->pim->gm_socket == -1) return; if (PIM_DEBUG_IGMP_TRACE) @@ -1938,45 +1958,14 @@ static void gm_trigger_specific(struct gm_sg *sg) } } -static void gm_start(struct interface *ifp) +static void gm_vrf_socket_incref(struct pim_instance *pim) { - struct pim_interface *pim_ifp = ifp->info; - struct gm_if *gm_ifp; + struct vrf *vrf = pim->vrf; int ret, intval; struct icmp6_filter filter[1]; - assert(pim_ifp); - assert(pim_ifp->pim); - assert(pim_ifp->mroute_vif_index >= 0); - assert(!pim_ifp->mld); - - gm_ifp = XCALLOC(MTYPE_GM_IFACE, sizeof(*gm_ifp)); - gm_ifp->ifp = ifp; - pim_ifp->mld = gm_ifp; - gm_ifp->pim = pim_ifp->pim; - monotime(&gm_ifp->started); - - zlog_info(log_ifp("starting MLD")); - - if (pim_ifp->mld_version == 1) - gm_ifp->cur_version = GM_MLDV1; - else - gm_ifp->cur_version = GM_MLDV2; - - /* hardcoded for dev without CLI */ - gm_ifp->cur_qrv = 2; - gm_ifp->cur_query_intv = pim_ifp->gm_default_query_interval * 1000; - gm_ifp->cur_query_intv_trig = gm_ifp->cur_query_intv; - gm_ifp->cur_max_resp = 250; - - gm_ifp->cfg_timing_fuzz.tv_sec = 0; - gm_ifp->cfg_timing_fuzz.tv_usec = 10 * 1000; - - gm_sgs_init(gm_ifp->sgs); - gm_subscribers_init(gm_ifp->subscribers); - gm_packet_expires_init(gm_ifp->expires); - gm_grp_pends_init(gm_ifp->grp_pends); - gm_gsq_pends_init(gm_ifp->gsq_pends); + if (pim->gm_socket_if_count++ && pim->gm_socket != -1) + return; ICMP6_FILTER_SETBLOCKALL(filter); ICMP6_FILTER_SETPASS(ICMP6_MLD_QUERY, filter); @@ -1985,54 +1974,56 @@ static void gm_start(struct interface *ifp) ICMP6_FILTER_SETPASS(ICMP6_MLD_V2_REPORT, filter); frr_with_privs (&pimd_privs) { - gm_ifp->sock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6); - if (gm_ifp->sock < 0) { - zlog_err("(%s) could not create MLD socket: %m", - ifp->name); + pim->gm_socket = vrf_socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6, + vrf->vrf_id, vrf->name); + if (pim->gm_socket < 0) { + zlog_err("(VRF %s) could not create MLD socket: %m", + vrf->name); return; } - ret = setsockopt(gm_ifp->sock, SOL_ICMPV6, ICMP6_FILTER, filter, - sizeof(filter)); + ret = setsockopt(pim->gm_socket, SOL_ICMPV6, ICMP6_FILTER, + filter, sizeof(filter)); if (ret) - zlog_err("(%s) failed to set ICMP6_FILTER: %m", - ifp->name); + zlog_err("(VRF %s) failed to set ICMP6_FILTER: %m", + vrf->name); intval = 1; - ret = setsockopt(gm_ifp->sock, SOL_IPV6, IPV6_RECVPKTINFO, + ret = setsockopt(pim->gm_socket, SOL_IPV6, IPV6_RECVPKTINFO, &intval, sizeof(intval)); if (ret) - zlog_err("(%s) failed to set IPV6_RECVPKTINFO: %m", - ifp->name); + zlog_err("(VRF %s) failed to set IPV6_RECVPKTINFO: %m", + vrf->name); intval = 1; - ret = setsockopt(gm_ifp->sock, SOL_IPV6, IPV6_RECVHOPOPTS, + ret = setsockopt(pim->gm_socket, SOL_IPV6, IPV6_RECVHOPOPTS, &intval, sizeof(intval)); if (ret) - zlog_err("(%s) failed to set IPV6_HOPOPTS: %m", - ifp->name); + zlog_err("(VRF %s) failed to set IPV6_HOPOPTS: %m", + vrf->name); intval = 1; - ret = setsockopt(gm_ifp->sock, SOL_IPV6, IPV6_RECVHOPLIMIT, + ret = setsockopt(pim->gm_socket, SOL_IPV6, IPV6_RECVHOPLIMIT, &intval, sizeof(intval)); if (ret) - zlog_err("(%s) failed to set IPV6_HOPLIMIT: %m", - ifp->name); + zlog_err("(VRF %s) failed to set IPV6_HOPLIMIT: %m", + vrf->name); intval = 1; - ret = setsockopt(gm_ifp->sock, SOL_IPV6, IPV6_MULTICAST_LOOP, + ret = setsockopt(pim->gm_socket, SOL_IPV6, IPV6_MULTICAST_LOOP, &intval, sizeof(intval)); if (ret) zlog_err( - "(%s) failed to disable IPV6_MULTICAST_LOOP: %m", - ifp->name); + "(VRF %s) failed to disable IPV6_MULTICAST_LOOP: %m", + vrf->name); intval = 1; - ret = setsockopt(gm_ifp->sock, SOL_IPV6, IPV6_MULTICAST_HOPS, + ret = setsockopt(pim->gm_socket, SOL_IPV6, IPV6_MULTICAST_HOPS, &intval, sizeof(intval)); if (ret) - zlog_err("(%s) failed to set IPV6_MULTICAST_HOPS: %m", - ifp->name); + zlog_err( + "(VRF %s) failed to set IPV6_MULTICAST_HOPS: %m", + vrf->name); /* NB: IPV6_MULTICAST_ALL does not completely bypass multicast * RX filtering in Linux. It only means "receive all groups @@ -2050,27 +2041,81 @@ static void gm_start(struct interface *ifp) * erroneously don't add that option is just not possible. */ intval = 1; - ret = setsockopt(gm_ifp->sock, SOL_IPV6, IPV6_MULTICAST_ALL, + ret = setsockopt(pim->gm_socket, SOL_IPV6, IPV6_MULTICAST_ALL, &intval, sizeof(intval)); if (ret) zlog_info( - "(%s) failed to set IPV6_MULTICAST_ALL: %m (OK on old kernels)", - ifp->name); + "(VRF %s) failed to set IPV6_MULTICAST_ALL: %m (OK on old kernels)", + vrf->name); + } + thread_add_read(router->master, gm_t_recv, pim, pim->gm_socket, + &pim->t_gm_recv); +} + +static void gm_vrf_socket_decref(struct pim_instance *pim) +{ + if (--pim->gm_socket_if_count) + return; + + THREAD_OFF(pim->t_gm_recv); + close(pim->gm_socket); + pim->gm_socket = -1; +} + +static void gm_start(struct interface *ifp) +{ + struct pim_interface *pim_ifp = ifp->info; + struct gm_if *gm_ifp; + + assert(pim_ifp); + assert(pim_ifp->pim); + assert(pim_ifp->mroute_vif_index >= 0); + assert(!pim_ifp->mld); + + gm_vrf_socket_incref(pim_ifp->pim); + + gm_ifp = XCALLOC(MTYPE_GM_IFACE, sizeof(*gm_ifp)); + gm_ifp->ifp = ifp; + pim_ifp->mld = gm_ifp; + gm_ifp->pim = pim_ifp->pim; + monotime(&gm_ifp->started); + + zlog_info(log_ifp("starting MLD")); + + if (pim_ifp->mld_version == 1) + gm_ifp->cur_version = GM_MLDV1; + else + gm_ifp->cur_version = GM_MLDV2; + + /* hardcoded for dev without CLI */ + gm_ifp->cur_qrv = 2; + gm_ifp->cur_query_intv = pim_ifp->gm_default_query_interval * 1000; + gm_ifp->cur_query_intv_trig = gm_ifp->cur_query_intv; + gm_ifp->cur_max_resp = 250; + + gm_ifp->cfg_timing_fuzz.tv_sec = 0; + gm_ifp->cfg_timing_fuzz.tv_usec = 10 * 1000; + + gm_sgs_init(gm_ifp->sgs); + gm_subscribers_init(gm_ifp->subscribers); + gm_packet_expires_init(gm_ifp->expires); + gm_grp_pends_init(gm_ifp->grp_pends); + gm_gsq_pends_init(gm_ifp->gsq_pends); + + frr_with_privs (&pimd_privs) { struct ipv6_mreq mreq; + int ret; /* all-MLDv2 group */ mreq.ipv6mr_multiaddr = gm_all_routers; mreq.ipv6mr_interface = ifp->ifindex; - ret = setsockopt(gm_ifp->sock, SOL_IPV6, IPV6_JOIN_GROUP, &mreq, - sizeof(mreq)); + ret = setsockopt(gm_ifp->pim->gm_socket, SOL_IPV6, + IPV6_JOIN_GROUP, &mreq, sizeof(mreq)); if (ret) zlog_err("(%s) failed to join ff02::16 (all-MLDv2): %m", ifp->name); } - - thread_add_read(router->master, gm_t_recv, gm_ifp, gm_ifp->sock, - &gm_ifp->t_recv); } void gm_ifp_teardown(struct interface *ifp) @@ -2093,14 +2138,25 @@ void gm_ifp_teardown(struct interface *ifp) THREAD_OFF(gm_ifp->t_query); THREAD_OFF(gm_ifp->t_other_querier); - THREAD_OFF(gm_ifp->t_recv); THREAD_OFF(gm_ifp->t_expire); - if (gm_ifp->sock != -1) { - close(gm_ifp->sock); - gm_ifp->sock = -1; + frr_with_privs (&pimd_privs) { + struct ipv6_mreq mreq; + int ret; + + /* all-MLDv2 group */ + mreq.ipv6mr_multiaddr = gm_all_routers; + mreq.ipv6mr_interface = ifp->ifindex; + ret = setsockopt(gm_ifp->pim->gm_socket, SOL_IPV6, + IPV6_LEAVE_GROUP, &mreq, sizeof(mreq)); + if (ret) + zlog_err( + "(%s) failed to leave ff02::16 (all-MLDv2): %m", + ifp->name); } + gm_vrf_socket_decref(gm_ifp->pim); + while ((pkt = gm_packet_expires_first(gm_ifp->expires))) gm_packet_drop(pkt, false); @@ -2143,8 +2199,6 @@ static void gm_update_ll(struct interface *ifp) { struct pim_interface *pim_ifp = ifp->info; struct gm_if *gm_ifp = pim_ifp ? pim_ifp->mld : NULL; - struct sockaddr_in6 sa = {.sin6_family = AF_INET6}; - int rc; bool was_querier; was_querier = @@ -2174,17 +2228,6 @@ static void gm_update_ll(struct interface *ifp) } else return; - /* we're querier */ - sa.sin6_addr = pim_ifp->ll_lowest; - sa.sin6_scope_id = ifp->ifindex; - - frr_with_privs (&pimd_privs) { - rc = bind(gm_ifp->sock, (struct sockaddr *)&sa, sizeof(sa)); - } - if (rc) - zlog_err(log_ifp("bind to %pPA failed: %m"), - &pim_ifp->ll_lowest); - gm_ifp->n_startup = gm_ifp->cur_qrv; thread_execute(router->master, gm_t_query, gm_ifp, 0); } @@ -2462,7 +2505,6 @@ static void gm_show_stats_one(struct vty *vty, struct gm_if *gm_ifp, { "v1 *,G queries sent", "txV1QueryGroup", &stats->tx_query_old_group }, { "TX errors", "txErrors", &stats->tx_query_fail }, - { "RX system errors", "rxErrorSys", &stats->rx_drop_sys }, { "RX dropped (checksum error)", "rxDropChecksum", &stats->rx_drop_csum }, { "RX dropped (invalid source)", "rxDropSrcAddr", &stats->rx_drop_srcaddr }, { "RX dropped (invalid dest.)", "rxDropDstAddr", &stats->rx_drop_dstaddr }, @@ -2824,7 +2866,6 @@ DEFPY(gm_debug_show, vty_out(vty, "ll_lowest: %pPA\n\n", &pim_ifp->ll_lowest); vty_out(vty, "t_query: %pTHD\n", gm_ifp->t_query); vty_out(vty, "t_other_querier: %pTHD\n", gm_ifp->t_other_querier); - vty_out(vty, "t_recv: %pTHD\n", gm_ifp->t_recv); vty_out(vty, "t_expire: %pTHD\n", gm_ifp->t_expire); vty_out(vty, "\nn_pending: %u\n", gm_ifp->n_pending); diff --git a/pimd/pim6_mld.h b/pimd/pim6_mld.h index 7ae963aa7..9c7a63700 100644 --- a/pimd/pim6_mld.h +++ b/pimd/pim6_mld.h @@ -282,7 +282,6 @@ enum gm_version { }; struct gm_if_stats { - uint64_t rx_drop_sys; uint64_t rx_drop_csum; uint64_t rx_drop_srcaddr; uint64_t rx_drop_dstaddr; @@ -314,7 +313,7 @@ struct gm_if_stats { struct gm_if { struct interface *ifp; struct pim_instance *pim; - struct thread *t_query, *t_other_querier, *t_recv, *t_expire; + struct thread *t_query, *t_other_querier, *t_expire; bool stopping; @@ -339,8 +338,6 @@ struct gm_if { struct gm_grp_pends_head grp_pends[1]; struct gm_gsq_pends_head gsq_pends[1]; - int sock; - pim_addr querier; pim_addr cur_ll_lowest; diff --git a/pimd/pim_instance.c b/pimd/pim_instance.c index 14f984508..d54add45a 100644 --- a/pimd/pim_instance.c +++ b/pimd/pim_instance.c @@ -114,6 +114,8 @@ static struct pim_instance *pim_instance_init(struct vrf *vrf) pim->send_v6_secondary = 1; + pim->gm_socket = -1; + pim_rp_init(pim); pim_bsm_proc_init(pim); diff --git a/pimd/pim_instance.h b/pimd/pim_instance.h index f8323deda..b19e8208b 100644 --- a/pimd/pim_instance.h +++ b/pimd/pim_instance.h @@ -167,6 +167,10 @@ struct pim_instance { struct list *ssmpingd_list; pim_addr ssmpingd_group_addr; + unsigned int gm_socket_if_count; + int gm_socket; + struct thread *t_gm_recv; + unsigned int igmp_group_count; unsigned int igmp_watermark_limit; unsigned int keep_alive_time; @@ -194,6 +198,8 @@ struct pim_instance { int64_t nexthop_lookups; int64_t nexthop_lookups_avoided; int64_t last_route_change_time; + + uint64_t gm_rx_drop_sys; }; void pim_vrf_init(void); |