ip6mr: add netlink notifications on mrt6msg cache reports
authorJulien Gomes <julien@arista.com>
Tue, 20 Jun 2017 20:54:18 +0000 (13:54 -0700)
committerDavid S. Miller <davem@davemloft.net>
Wed, 21 Jun 2017 15:22:53 +0000 (11:22 -0400)
Add Netlink notifications on cache reports in ip6mr, in addition to the
existing mrt6msg sent to mroute6_sk.
Send RTM_NEWCACHEREPORT notifications to RTNLGRP_IPV6_MROUTE_R.

MSGTYPE, MIF_ID, SRC_ADDR and DST_ADDR Netlink attributes contain the
same data as their equivalent fields in the mrt6msg header.
PKT attribute is the packet sent to mroute6_sk, without the added
mrt6msg header.

Suggested-by: Ryan Halbrook <halbrook@arista.com>
Signed-off-by: Julien Gomes <julien@arista.com>
Reviewed-by: Nikolay Aleksandrov <nikolay@cumulusnetworks.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/uapi/linux/mroute6.h
net/ipv6/ip6mr.c

index ed57211..e474681 100644 (file)
@@ -133,4 +133,16 @@ struct mrt6msg {
        struct in6_addr im6_src, im6_dst;
 };
 
+/* ip6mr netlink cache report attributes */
+enum {
+       IP6MRA_CREPORT_UNSPEC,
+       IP6MRA_CREPORT_MSGTYPE,
+       IP6MRA_CREPORT_MIF_ID,
+       IP6MRA_CREPORT_SRC_ADDR,
+       IP6MRA_CREPORT_DST_ADDR,
+       IP6MRA_CREPORT_PKT,
+       __IP6MRA_CREPORT_MAX
+};
+#define IP6MRA_CREPORT_MAX (__IP6MRA_CREPORT_MAX - 1)
+
 #endif /* _UAPI__LINUX_MROUTE6_H */
index b0e2bf1..7454850 100644 (file)
@@ -116,6 +116,7 @@ static int __ip6mr_fill_mroute(struct mr6_table *mrt, struct sk_buff *skb,
                               struct mfc6_cache *c, struct rtmsg *rtm);
 static void mr6_netlink_event(struct mr6_table *mrt, struct mfc6_cache *mfc,
                              int cmd);
+static void mrt6msg_netlink_event(struct mr6_table *mrt, struct sk_buff *pkt);
 static int ip6mr_rtm_dumproute(struct sk_buff *skb,
                               struct netlink_callback *cb);
 static void mroute_clean_tables(struct mr6_table *mrt, bool all);
@@ -1125,8 +1126,7 @@ static void ip6mr_cache_resolve(struct net *net, struct mr6_table *mrt,
 }
 
 /*
- *     Bounce a cache query up to pim6sd. We could use netlink for this but pim6sd
- *     expects the following bizarre scheme.
+ *     Bounce a cache query up to pim6sd and netlink.
  *
  *     Called under mrt_lock.
  */
@@ -1208,6 +1208,8 @@ static int ip6mr_cache_report(struct mr6_table *mrt, struct sk_buff *pkt,
                return -EINVAL;
        }
 
+       mrt6msg_netlink_event(mrt, skb);
+
        /*
         *      Deliver to user space multicast routing algorithms
         */
@@ -2457,6 +2459,71 @@ errout:
                rtnl_set_sk_err(net, RTNLGRP_IPV6_MROUTE, err);
 }
 
+static size_t mrt6msg_netlink_msgsize(size_t payloadlen)
+{
+       size_t len =
+               NLMSG_ALIGN(sizeof(struct rtgenmsg))
+               + nla_total_size(1)     /* IP6MRA_CREPORT_MSGTYPE */
+               + nla_total_size(4)     /* IP6MRA_CREPORT_MIF_ID */
+                                       /* IP6MRA_CREPORT_SRC_ADDR */
+               + nla_total_size(sizeof(struct in6_addr))
+                                       /* IP6MRA_CREPORT_DST_ADDR */
+               + nla_total_size(sizeof(struct in6_addr))
+                                       /* IP6MRA_CREPORT_PKT */
+               + nla_total_size(payloadlen)
+               ;
+
+       return len;
+}
+
+static void mrt6msg_netlink_event(struct mr6_table *mrt, struct sk_buff *pkt)
+{
+       struct net *net = read_pnet(&mrt->net);
+       struct nlmsghdr *nlh;
+       struct rtgenmsg *rtgenm;
+       struct mrt6msg *msg;
+       struct sk_buff *skb;
+       struct nlattr *nla;
+       int payloadlen;
+
+       payloadlen = pkt->len - sizeof(struct mrt6msg);
+       msg = (struct mrt6msg *)skb_transport_header(pkt);
+
+       skb = nlmsg_new(mrt6msg_netlink_msgsize(payloadlen), GFP_ATOMIC);
+       if (!skb)
+               goto errout;
+
+       nlh = nlmsg_put(skb, 0, 0, RTM_NEWCACHEREPORT,
+                       sizeof(struct rtgenmsg), 0);
+       if (!nlh)
+               goto errout;
+       rtgenm = nlmsg_data(nlh);
+       rtgenm->rtgen_family = RTNL_FAMILY_IP6MR;
+       if (nla_put_u8(skb, IP6MRA_CREPORT_MSGTYPE, msg->im6_msgtype) ||
+           nla_put_u32(skb, IP6MRA_CREPORT_MIF_ID, msg->im6_mif) ||
+           nla_put_in6_addr(skb, IP6MRA_CREPORT_SRC_ADDR,
+                            &msg->im6_src) ||
+           nla_put_in6_addr(skb, IP6MRA_CREPORT_DST_ADDR,
+                            &msg->im6_dst))
+               goto nla_put_failure;
+
+       nla = nla_reserve(skb, IP6MRA_CREPORT_PKT, payloadlen);
+       if (!nla || skb_copy_bits(pkt, sizeof(struct mrt6msg),
+                                 nla_data(nla), payloadlen))
+               goto nla_put_failure;
+
+       nlmsg_end(skb, nlh);
+
+       rtnl_notify(skb, net, 0, RTNLGRP_IPV6_MROUTE_R, NULL, GFP_ATOMIC);
+       return;
+
+nla_put_failure:
+       nlmsg_cancel(skb, nlh);
+errout:
+       kfree_skb(skb);
+       rtnl_set_sk_err(net, RTNLGRP_IPV6_MROUTE_R, -ENOBUFS);
+}
+
 static int ip6mr_rtm_dumproute(struct sk_buff *skb, struct netlink_callback *cb)
 {
        struct net *net = sock_net(skb->sk);