Currently, all ipmr/ip6mr cache reports are sent through the
mroute/mroute6 socket only.
This forces the use of a single socket for mroute programming, cache
reports and, regarding ipmr, IGMP messages without Router Alert option
reception.
The present patches are aiming to send Netlink notifications in addition
to the existing igmpmsg/mrt6msg to give user programs a way to handle
cache reports in parallel with multiple sockets other than the
mroute/mroute6 socket.
Changes in v2:
- Changed attributes naming from {IPMRA,IP6MRA}_CACHEREPORTA_* to
{IPMRA,IP6MRA}_CREPORT_*
- Improved packet data copy to handle non-linear packets in
ipmr/ip6mr cache report Netlink notification creation
- Added two rtnetlink groups with restricted-binding
- Changed cache report notified groups from RTNL_{IPV4,IPV6}_MROUTE to
the new restricted groups in ipmr/ip6mr
Julien Gomes (4):
rtnetlink: add NEWCACHEREPORT message type
rtnetlink: add restricted rtnl groups for ipv4 and ipv6 mroute
ipmr: add netlink notifications on igmpmsg cache reports
ip6mr: add netlink notifications on mrt6msg cache reports
include/uapi/linux/mroute.h | 12 ++++++++
include/uapi/linux/mroute6.h | 12 ++++++++
include/uapi/linux/rtnetlink.h | 7 +++++
net/core/rtnetlink.c | 13 ++++++++
net/ipv4/ipmr.c | 67 ++++++++++++++++++++++++++++++++++++++++--
net/ipv6/ip6mr.c | 67 ++++++++++++++++++++++++++++++++++++++++--
security/selinux/nlmsgtab.c | 3 +-
7 files changed, 176 insertions(+), 5 deletions(-)
--
2.13.1
Add RTNLGRP_{IPV4,IPV6}_MROUTE_R as two new restricted groups for the
NETLINK_ROUTE family.
Binding to these groups specifically requires CAP_NET_ADMIN to allow
multicast of sensitive messages (e.g. mroute cache reports).
Signed-off-by: Julien Gomes <[email protected]>
---
include/uapi/linux/rtnetlink.h | 4 ++++
net/core/rtnetlink.c | 13 +++++++++++++
2 files changed, 17 insertions(+)
diff --git a/include/uapi/linux/rtnetlink.h b/include/uapi/linux/rtnetlink.h
index cd1afb900929..d148505010a7 100644
--- a/include/uapi/linux/rtnetlink.h
+++ b/include/uapi/linux/rtnetlink.h
@@ -669,6 +669,10 @@ enum rtnetlink_groups {
#define RTNLGRP_NSID RTNLGRP_NSID
RTNLGRP_MPLS_NETCONF,
#define RTNLGRP_MPLS_NETCONF RTNLGRP_MPLS_NETCONF
+ RTNLGRP_IPV4_MROUTE_R,
+#define RTNLGRP_IPV4_MROUTE_R RTNLGRP_IPV4_MROUTE_R
+ RTNLGRP_IPV6_MROUTE_R,
+#define RTNLGRP_IPV6_MROUTE_R RTNLGRP_IPV6_MROUTE_R
__RTNLGRP_MAX
};
#define RTNLGRP_MAX (__RTNLGRP_MAX - 1)
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
index 3aa57848a895..4aefa5a2625f 100644
--- a/net/core/rtnetlink.c
+++ b/net/core/rtnetlink.c
@@ -4218,6 +4218,18 @@ static void rtnetlink_rcv(struct sk_buff *skb)
rtnl_unlock();
}
+static int rtnetlink_bind(struct net *net, int group)
+{
+ switch (group) {
+ case RTNLGRP_IPV4_MROUTE_R:
+ case RTNLGRP_IPV6_MROUTE_R:
+ if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
+ return -EPERM;
+ break;
+ }
+ return 0;
+}
+
static int rtnetlink_event(struct notifier_block *this, unsigned long event, void *ptr)
{
struct net_device *dev = netdev_notifier_info_to_dev(ptr);
@@ -4252,6 +4264,7 @@ static int __net_init rtnetlink_net_init(struct net *net)
.input = rtnetlink_rcv,
.cb_mutex = &rtnl_mutex,
.flags = NL_CFG_F_NONROOT_RECV,
+ .bind = rtnetlink_bind,
};
sk = netlink_kernel_create(net, NETLINK_ROUTE, &cfg);
--
2.13.1
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 <[email protected]>
Signed-off-by: Julien Gomes <[email protected]>
---
include/uapi/linux/mroute6.h | 12 ++++++++
net/ipv6/ip6mr.c | 67 ++++++++++++++++++++++++++++++++++++++++++--
2 files changed, 77 insertions(+), 2 deletions(-)
diff --git a/include/uapi/linux/mroute6.h b/include/uapi/linux/mroute6.h
index ed5721148768..e4746816c855 100644
--- a/include/uapi/linux/mroute6.h
+++ b/include/uapi/linux/mroute6.h
@@ -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 */
diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c
index b0e2bf1f4212..28a1fb49f12e 100644
--- a/net/ipv6/ip6mr.c
+++ b/net/ipv6/ip6mr.c
@@ -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,67 @@ static void mr6_netlink_event(struct mr6_table *mrt, struct mfc6_cache *mfc,
rtnl_set_sk_err(net, RTNLGRP_IPV6_MROUTE, err);
}
+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;
+ int msgsize;
+
+ payloadlen = pkt->len - sizeof(struct mrt6msg);
+ msg = (struct mrt6msg *)skb_transport_header(pkt);
+ msgsize = NLMSG_ALIGN(sizeof(struct rtgenmsg))
+ + nla_total_size(1)
+ /* IP6MRA_CREPORT_MSGTYPE */
+ + nla_total_size(2)
+ /* IP6MRA_CREPORT_MIF_ID */
+ + nla_total_size(sizeof(struct in6_addr))
+ /* IP6MRA_CREPORT_SRC_ADDR */
+ + nla_total_size(sizeof(struct in6_addr))
+ /* IP6MRA_CREPORT_DST_ADDR */
+ + nla_total_size(payloadlen)
+ /* IP6MRA_CREPORT_PKT */
+ ;
+
+ skb = nlmsg_new(msgsize, 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_u16(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);
--
2.13.1
Add Netlink notifications on cache reports in ipmr, in addition to the
existing igmpmsg sent to mroute_sk.
Send RTM_NEWCACHEREPORT notifications to RTNLGRP_IPV4_MROUTE_R.
MSGTYPE, VIF_ID, SRC_ADDR and DST_ADDR Netlink attributes contain the
same data as their equivalent fields in the igmpmsg header.
PKT attribute is the packet sent to mroute_sk, without the added igmpmsg
header.
Suggested-by: Ryan Halbrook <[email protected]>
Signed-off-by: Julien Gomes <[email protected]>
---
include/uapi/linux/mroute.h | 12 ++++++++
net/ipv4/ipmr.c | 67 +++++++++++++++++++++++++++++++++++++++++++--
2 files changed, 77 insertions(+), 2 deletions(-)
diff --git a/include/uapi/linux/mroute.h b/include/uapi/linux/mroute.h
index f904367c0cee..e8e5041dea8e 100644
--- a/include/uapi/linux/mroute.h
+++ b/include/uapi/linux/mroute.h
@@ -152,6 +152,18 @@ enum {
};
#define IPMRA_VIFA_MAX (__IPMRA_VIFA_MAX - 1)
+/* ipmr netlink cache report attributes */
+enum {
+ IPMRA_CREPORT_UNSPEC,
+ IPMRA_CREPORT_MSGTYPE,
+ IPMRA_CREPORT_VIF_ID,
+ IPMRA_CREPORT_SRC_ADDR,
+ IPMRA_CREPORT_DST_ADDR,
+ IPMRA_CREPORT_PKT,
+ __IPMRA_CREPORT_MAX
+};
+#define IPMRA_CREPORT_MAX (__IPMRA_CREPORT_MAX - 1)
+
/* That's all usermode folks */
#define MFC_ASSERT_THRESH (3*HZ) /* Maximal freq. of asserts */
diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c
index 3e7454aa49e8..1e591bcaad6d 100644
--- a/net/ipv4/ipmr.c
+++ b/net/ipv4/ipmr.c
@@ -109,6 +109,7 @@ static int __ipmr_fill_mroute(struct mr_table *mrt, struct sk_buff *skb,
struct mfc_cache *c, struct rtmsg *rtm);
static void mroute_netlink_event(struct mr_table *mrt, struct mfc_cache *mfc,
int cmd);
+static void igmpmsg_netlink_event(struct mr_table *mrt, struct sk_buff *pkt);
static void mroute_clean_tables(struct mr_table *mrt, bool all);
static void ipmr_expire_process(unsigned long arg);
@@ -995,8 +996,7 @@ static void ipmr_cache_resolve(struct net *net, struct mr_table *mrt,
}
}
-/* Bounce a cache query up to mrouted. We could use netlink for this but mrouted
- * expects the following bizarre scheme.
+/* Bounce a cache query up to mrouted and netlink.
*
* Called under mrt_lock.
*/
@@ -1062,6 +1062,8 @@ static int ipmr_cache_report(struct mr_table *mrt,
return -EINVAL;
}
+ igmpmsg_netlink_event(mrt, skb);
+
/* Deliver to mrouted */
ret = sock_queue_rcv_skb(mroute_sk, skb);
rcu_read_unlock();
@@ -2341,6 +2343,67 @@ static void mroute_netlink_event(struct mr_table *mrt, struct mfc_cache *mfc,
rtnl_set_sk_err(net, RTNLGRP_IPV4_MROUTE, err);
}
+static void igmpmsg_netlink_event(struct mr_table *mrt, struct sk_buff *pkt)
+{
+ struct net *net = read_pnet(&mrt->net);
+ struct nlmsghdr *nlh;
+ struct rtgenmsg *rtgenm;
+ struct igmpmsg *msg;
+ struct sk_buff *skb;
+ struct nlattr *nla;
+ int payloadlen;
+ int msgsize;
+
+ payloadlen = pkt->len - sizeof(struct igmpmsg);
+ msg = (struct igmpmsg *)skb_network_header(pkt);
+ msgsize = NLMSG_ALIGN(sizeof(struct rtgenmsg))
+ + nla_total_size(1)
+ /* IPMRA_CREPORT_MSGTYPE */
+ + nla_total_size(1)
+ /* IPMRA_CREPORT_VIF_ID */
+ + nla_total_size(4)
+ /* IPMRA_CREPORT_SRC_ADDR */
+ + nla_total_size(4)
+ /* IPMRA_CREPORT_DST_ADDR */
+ + nla_total_size(payloadlen)
+ /* IPMRA_CREPORT_PKT */
+ ;
+
+ skb = nlmsg_new(msgsize, 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_IPMR;
+ if (nla_put_u8(skb, IPMRA_CREPORT_MSGTYPE, msg->im_msgtype) ||
+ nla_put_u8(skb, IPMRA_CREPORT_VIF_ID, msg->im_vif) ||
+ nla_put_in_addr(skb, IPMRA_CREPORT_SRC_ADDR,
+ msg->im_src.s_addr) ||
+ nla_put_in_addr(skb, IPMRA_CREPORT_DST_ADDR,
+ msg->im_dst.s_addr))
+ goto nla_put_failure;
+
+ nla = nla_reserve(skb, IPMRA_CREPORT_PKT, payloadlen);
+ if (!nla || skb_copy_bits(pkt, sizeof(struct igmpmsg),
+ nla_data(nla), payloadlen))
+ goto nla_put_failure;
+
+ nlmsg_end(skb, nlh);
+
+ rtnl_notify(skb, net, 0, RTNLGRP_IPV4_MROUTE_R, NULL, GFP_ATOMIC);
+ return;
+
+nla_put_failure:
+ nlmsg_cancel(skb, nlh);
+errout:
+ kfree_skb(skb);
+ rtnl_set_sk_err(net, RTNLGRP_IPV4_MROUTE_R, -ENOBUFS);
+}
+
static int ipmr_rtm_dumproute(struct sk_buff *skb, struct netlink_callback *cb)
{
struct net *net = sock_net(skb->sk);
--
2.13.1
New NEWCACHEREPORT message type to be used for cache reports sent
via Netlink, effectively allowing splitting cache report reception from
mroute programming.
Suggested-by: Ryan Halbrook <[email protected]>
Signed-off-by: Julien Gomes <[email protected]>
---
include/uapi/linux/rtnetlink.h | 3 +++
security/selinux/nlmsgtab.c | 3 ++-
2 files changed, 5 insertions(+), 1 deletion(-)
diff --git a/include/uapi/linux/rtnetlink.h b/include/uapi/linux/rtnetlink.h
index 564790e854f7..cd1afb900929 100644
--- a/include/uapi/linux/rtnetlink.h
+++ b/include/uapi/linux/rtnetlink.h
@@ -146,6 +146,9 @@ enum {
RTM_GETSTATS = 94,
#define RTM_GETSTATS RTM_GETSTATS
+ RTM_NEWCACHEREPORT = 96,
+#define RTM_NEWCACHEREPORT RTM_NEWCACHEREPORT
+
__RTM_MAX,
#define RTM_MAX (((__RTM_MAX + 3) & ~3) - 1)
};
diff --git a/security/selinux/nlmsgtab.c b/security/selinux/nlmsgtab.c
index 5aeaf30b7a13..7b7433a1a34c 100644
--- a/security/selinux/nlmsgtab.c
+++ b/security/selinux/nlmsgtab.c
@@ -79,6 +79,7 @@ static const struct nlmsg_perm nlmsg_route_perms[] =
{ RTM_GETNSID, NETLINK_ROUTE_SOCKET__NLMSG_READ },
{ RTM_NEWSTATS, NETLINK_ROUTE_SOCKET__NLMSG_READ },
{ RTM_GETSTATS, NETLINK_ROUTE_SOCKET__NLMSG_READ },
+ { RTM_NEWCACHEREPORT, NETLINK_ROUTE_SOCKET__NLMSG_READ },
};
static const struct nlmsg_perm nlmsg_tcpdiag_perms[] =
@@ -158,7 +159,7 @@ int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm)
switch (sclass) {
case SECCLASS_NETLINK_ROUTE_SOCKET:
/* RTM_MAX always point to RTM_SETxxxx, ie RTM_NEWxxx + 3 */
- BUILD_BUG_ON(RTM_MAX != (RTM_NEWSTATS + 3));
+ BUILD_BUG_ON(RTM_MAX != (RTM_NEWCACHEREPORT + 3));
err = nlmsg_perm(nlmsg_type, perm, nlmsg_route_perms,
sizeof(nlmsg_route_perms));
break;
--
2.13.1
On 19/06/17 23:44, Julien Gomes wrote:
> New NEWCACHEREPORT message type to be used for cache reports sent
> via Netlink, effectively allowing splitting cache report reception from
> mroute programming.
>
> Suggested-by: Ryan Halbrook <[email protected]>
> Signed-off-by: Julien Gomes <[email protected]>
> ---
> include/uapi/linux/rtnetlink.h | 3 +++
> security/selinux/nlmsgtab.c | 3 ++-
> 2 files changed, 5 insertions(+), 1 deletion(-)
>
Reviewed-by: Nikolay Aleksandrov <[email protected]>
On 19/06/17 23:44, Julien Gomes wrote:
> Add RTNLGRP_{IPV4,IPV6}_MROUTE_R as two new restricted groups for the
> NETLINK_ROUTE family.
> Binding to these groups specifically requires CAP_NET_ADMIN to allow
> multicast of sensitive messages (e.g. mroute cache reports).
>
> Signed-off-by: Julien Gomes <[email protected]>
> ---
> include/uapi/linux/rtnetlink.h | 4 ++++
> net/core/rtnetlink.c | 13 +++++++++++++
> 2 files changed, 17 insertions(+)
Thanks!
Suggested-by: Nikolay Aleksandrov <[email protected]>
Signed-off-by: Nikolay Aleksandrov <[email protected]>
On 19/06/17 23:44, Julien Gomes wrote:
> Add Netlink notifications on cache reports in ipmr, in addition to the
> existing igmpmsg sent to mroute_sk.
> Send RTM_NEWCACHEREPORT notifications to RTNLGRP_IPV4_MROUTE_R.
>
> MSGTYPE, VIF_ID, SRC_ADDR and DST_ADDR Netlink attributes contain the
> same data as their equivalent fields in the igmpmsg header.
> PKT attribute is the packet sent to mroute_sk, without the added igmpmsg
> header.
>
> Suggested-by: Ryan Halbrook <[email protected]>
> Signed-off-by: Julien Gomes <[email protected]>
> ---
> include/uapi/linux/mroute.h | 12 ++++++++
> net/ipv4/ipmr.c | 67 +++++++++++++++++++++++++++++++++++++++++++--
> 2 files changed, 77 insertions(+), 2 deletions(-)
>
> diff --git a/include/uapi/linux/mroute.h b/include/uapi/linux/mroute.h
> index f904367c0cee..e8e5041dea8e 100644
> --- a/include/uapi/linux/mroute.h
> +++ b/include/uapi/linux/mroute.h
> @@ -152,6 +152,18 @@ enum {
> };
> #define IPMRA_VIFA_MAX (__IPMRA_VIFA_MAX - 1)
>
> +/* ipmr netlink cache report attributes */
> +enum {
> + IPMRA_CREPORT_UNSPEC,
> + IPMRA_CREPORT_MSGTYPE,
> + IPMRA_CREPORT_VIF_ID,
> + IPMRA_CREPORT_SRC_ADDR,
> + IPMRA_CREPORT_DST_ADDR,
> + IPMRA_CREPORT_PKT,
> + __IPMRA_CREPORT_MAX
> +};
> +#define IPMRA_CREPORT_MAX (__IPMRA_CREPORT_MAX - 1)
> +
> /* That's all usermode folks */
>
> #define MFC_ASSERT_THRESH (3*HZ) /* Maximal freq. of asserts */
> diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c
> index 3e7454aa49e8..1e591bcaad6d 100644
> --- a/net/ipv4/ipmr.c
> +++ b/net/ipv4/ipmr.c
> @@ -109,6 +109,7 @@ static int __ipmr_fill_mroute(struct mr_table *mrt, struct sk_buff *skb,
> struct mfc_cache *c, struct rtmsg *rtm);
> static void mroute_netlink_event(struct mr_table *mrt, struct mfc_cache *mfc,
> int cmd);
> +static void igmpmsg_netlink_event(struct mr_table *mrt, struct sk_buff *pkt);
> static void mroute_clean_tables(struct mr_table *mrt, bool all);
> static void ipmr_expire_process(unsigned long arg);
>
> @@ -995,8 +996,7 @@ static void ipmr_cache_resolve(struct net *net, struct mr_table *mrt,
> }
> }
>
> -/* Bounce a cache query up to mrouted. We could use netlink for this but mrouted
> - * expects the following bizarre scheme.
> +/* Bounce a cache query up to mrouted and netlink.
> *
> * Called under mrt_lock.
> */
> @@ -1062,6 +1062,8 @@ static int ipmr_cache_report(struct mr_table *mrt,
> return -EINVAL;
> }
>
> + igmpmsg_netlink_event(mrt, skb);
> +
> /* Deliver to mrouted */
> ret = sock_queue_rcv_skb(mroute_sk, skb);
> rcu_read_unlock();
> @@ -2341,6 +2343,67 @@ static void mroute_netlink_event(struct mr_table *mrt, struct mfc_cache *mfc,
> rtnl_set_sk_err(net, RTNLGRP_IPV4_MROUTE, err);
> }
>
> +static void igmpmsg_netlink_event(struct mr_table *mrt, struct sk_buff *pkt)
> +{
> + struct net *net = read_pnet(&mrt->net);
> + struct nlmsghdr *nlh;
> + struct rtgenmsg *rtgenm;
> + struct igmpmsg *msg;
> + struct sk_buff *skb;
> + struct nlattr *nla;
> + int payloadlen;
> + int msgsize;
> +
> + payloadlen = pkt->len - sizeof(struct igmpmsg);
> + msg = (struct igmpmsg *)skb_network_header(pkt);
> + msgsize = NLMSG_ALIGN(sizeof(struct rtgenmsg))
> + + nla_total_size(1)
> + /* IPMRA_CREPORT_MSGTYPE */
> + + nla_total_size(1)
> + /* IPMRA_CREPORT_VIF_ID */
> + + nla_total_size(4)
> + /* IPMRA_CREPORT_SRC_ADDR */
> + + nla_total_size(4)
> + /* IPMRA_CREPORT_DST_ADDR */
> + + nla_total_size(payloadlen)
> + /* IPMRA_CREPORT_PKT */
> + ;
If this ends up having another version you could pull this size
calculation into a separate function. E.g. see mroute_msgsize
> +
> + skb = nlmsg_new(msgsize, 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_IPMR;
> + if (nla_put_u8(skb, IPMRA_CREPORT_MSGTYPE, msg->im_msgtype) ||
> + nla_put_u8(skb, IPMRA_CREPORT_VIF_ID, msg->im_vif) ||
This would effectively limit the new call to handle 255 ifaces. I used a u32
for the getlink interface's vif id, it'd be nice to be consistent and also allow
for more interfaces in the future (u32 might be too big, but we're not pressed for
space here).
> + nla_put_in_addr(skb, IPMRA_CREPORT_SRC_ADDR,
> + msg->im_src.s_addr) ||
> + nla_put_in_addr(skb, IPMRA_CREPORT_DST_ADDR,
> + msg->im_dst.s_addr))
> + goto nla_put_failure;
> +
> + nla = nla_reserve(skb, IPMRA_CREPORT_PKT, payloadlen);
> + if (!nla || skb_copy_bits(pkt, sizeof(struct igmpmsg),
> + nla_data(nla), payloadlen))
> + goto nla_put_failure;
> +
> + nlmsg_end(skb, nlh);
> +
> + rtnl_notify(skb, net, 0, RTNLGRP_IPV4_MROUTE_R, NULL, GFP_ATOMIC);
> + return;
> +
> +nla_put_failure:
> + nlmsg_cancel(skb, nlh);
> +errout:
> + kfree_skb(skb);
> + rtnl_set_sk_err(net, RTNLGRP_IPV4_MROUTE_R, -ENOBUFS);
> +}
> +
> static int ipmr_rtm_dumproute(struct sk_buff *skb, struct netlink_callback *cb)
> {
> struct net *net = sock_net(skb->sk);
>
On 19/06/17 23:44, Julien Gomes wrote:
> 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 <[email protected]>
> Signed-off-by: Julien Gomes <[email protected]>
> ---
> include/uapi/linux/mroute6.h | 12 ++++++++
> net/ipv6/ip6mr.c | 67 ++++++++++++++++++++++++++++++++++++++++++--
> 2 files changed, 77 insertions(+), 2 deletions(-)
>
> diff --git a/include/uapi/linux/mroute6.h b/include/uapi/linux/mroute6.h
> index ed5721148768..e4746816c855 100644
> --- a/include/uapi/linux/mroute6.h
> +++ b/include/uapi/linux/mroute6.h
> @@ -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 */
> diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c
> index b0e2bf1f4212..28a1fb49f12e 100644
> --- a/net/ipv6/ip6mr.c
> +++ b/net/ipv6/ip6mr.c
> @@ -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,67 @@ static void mr6_netlink_event(struct mr6_table *mrt, struct mfc6_cache *mfc,
> rtnl_set_sk_err(net, RTNLGRP_IPV6_MROUTE, err);
> }
>
> +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;
> + int msgsize;
> +
> + payloadlen = pkt->len - sizeof(struct mrt6msg);
> + msg = (struct mrt6msg *)skb_transport_header(pkt);
> + msgsize = NLMSG_ALIGN(sizeof(struct rtgenmsg))
> + + nla_total_size(1)
> + /* IP6MRA_CREPORT_MSGTYPE */
> + + nla_total_size(2)
> + /* IP6MRA_CREPORT_MIF_ID */
> + + nla_total_size(sizeof(struct in6_addr))
> + /* IP6MRA_CREPORT_SRC_ADDR */
> + + nla_total_size(sizeof(struct in6_addr))
> + /* IP6MRA_CREPORT_DST_ADDR */
> + + nla_total_size(payloadlen)
> + /* IP6MRA_CREPORT_PKT */
> + ;
Same as patch 03, this calculation could be in a separate function.
> +
> + skb = nlmsg_new(msgsize, 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_u16(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);
>
From: Julien Gomes <[email protected]>
Date: Mon, 19 Jun 2017 13:44:13 -0700
> Currently, all ipmr/ip6mr cache reports are sent through the
> mroute/mroute6 socket only.
> This forces the use of a single socket for mroute programming, cache
> reports and, regarding ipmr, IGMP messages without Router Alert option
> reception.
>
> The present patches are aiming to send Netlink notifications in addition
> to the existing igmpmsg/mrt6msg to give user programs a way to handle
> cache reports in parallel with multiple sockets other than the
> mroute/mroute6 socket.
>
> Changes in v2:
> - Changed attributes naming from {IPMRA,IP6MRA}_CACHEREPORTA_* to
> {IPMRA,IP6MRA}_CREPORT_*
> - Improved packet data copy to handle non-linear packets in
> ipmr/ip6mr cache report Netlink notification creation
> - Added two rtnetlink groups with restricted-binding
> - Changed cache report notified groups from RTNL_{IPV4,IPV6}_MROUTE to
> the new restricted groups in ipmr/ip6mr
Please address Nikolay's feedback about interface number limits etc.
Thanks.