summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--pimd/pim6_mld.c239
-rw-r--r--pimd/pim6_mld.h5
-rw-r--r--pimd/pim_instance.c2
-rw-r--r--pimd/pim_instance.h6
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);