Merge branch 'ipmr-ip6mr-add-Netlink-notifications-on-cache-reports'

Julien Gomes says:

====================
ipmr/ip6mr: add Netlink notifications on cache reports

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

Changes in v3:
- Put message size calculation for {igmp,mrt6}msg_netlink_event in separate
  functions
- Increased vif id attributes size from u8 to u32
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2017-06-21 11:22:54 -04:00
commit 2f89a7953b
7 changed files with 182 additions and 5 deletions

View File

@ -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 */

View 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 */

View File

@ -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)
};
@ -666,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)

View File

@ -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);

View File

@ -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,69 @@ errout:
rtnl_set_sk_err(net, RTNLGRP_IPV4_MROUTE, err);
}
static size_t igmpmsg_netlink_msgsize(size_t payloadlen)
{
size_t len =
NLMSG_ALIGN(sizeof(struct rtgenmsg))
+ nla_total_size(1) /* IPMRA_CREPORT_MSGTYPE */
+ nla_total_size(4) /* IPMRA_CREPORT_VIF_ID */
+ nla_total_size(4) /* IPMRA_CREPORT_SRC_ADDR */
+ nla_total_size(4) /* IPMRA_CREPORT_DST_ADDR */
/* IPMRA_CREPORT_PKT */
+ nla_total_size(payloadlen)
;
return len;
}
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;
payloadlen = pkt->len - sizeof(struct igmpmsg);
msg = (struct igmpmsg *)skb_network_header(pkt);
skb = nlmsg_new(igmpmsg_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_IPMR;
if (nla_put_u8(skb, IPMRA_CREPORT_MSGTYPE, msg->im_msgtype) ||
nla_put_u32(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);

View 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);

View File

@ -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;