Netdev List
 help / color / mirror / Atom feed
* [PATCH 1/2] netlink: add netlink_kernel_cfg parameter to netlink_kernel_create
From: pablo @ 2012-06-29 16:15 UTC (permalink / raw)
  To: netfilter-devel; +Cc: davem, netdev
In-Reply-To: <1340986522-3442-1-git-send-email-pablo@netfilter.org>

From: Pablo Neira Ayuso <pablo@netfilter.org>

This patch adds the following structure:

struct netlink_kernel_cfg {
        unsigned int    groups;
        void            (*input)(struct sk_buff *skb);
        struct mutex    *cb_mutex;
};

That can be passed to netlink_kernel_create to set optional configurations
for netlink kernel sockets.

I've populated this structure by looking for NULL and zero parameters at the
existing code. The remaining parameters that always need to be set are still
left in the original interface.

That includes optional parameters for the netlink socket creation. This allows
easy extensibility of this interface in the future.

This patch also adapts all callers to use this new interface.

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 crypto/crypto_user.c                |    7 +++++--
 drivers/connector/connector.c       |   13 +++++++++----
 drivers/infiniband/core/netlink.c   |    7 +++++--
 drivers/scsi/scsi_netlink.c         |    7 +++++--
 drivers/scsi/scsi_transport_iscsi.c |    9 ++++++---
 drivers/staging/gdm72xx/netlink_k.c |    6 ++++--
 include/linux/netlink.h             |   15 ++++++++++-----
 kernel/audit.c                      |    7 +++++--
 lib/kobject_uevent.c                |    5 ++++-
 net/bridge/netfilter/ebt_ulog.c     |    6 ++++--
 net/core/rtnetlink.c                |    9 +++++++--
 net/core/sock_diag.c                |    8 ++++++--
 net/decnet/netfilter/dn_rtmsg.c     |    8 +++++---
 net/ipv4/fib_frontend.c             |    7 +++++--
 net/ipv4/netfilter/ipt_ULOG.c       |    8 +++++---
 net/netfilter/nfnetlink.c           |    7 +++++--
 net/netlink/af_netlink.c            |   16 ++++++++++------
 net/netlink/genetlink.c             |   10 +++++++---
 net/xfrm/xfrm_user.c                |    7 +++++--
 security/selinux/netlink.c          |    6 +++++-
 20 files changed, 117 insertions(+), 51 deletions(-)

diff --git a/crypto/crypto_user.c b/crypto/crypto_user.c
index 5a37ead..ba2c611 100644
--- a/crypto/crypto_user.c
+++ b/crypto/crypto_user.c
@@ -496,9 +496,12 @@ static void crypto_netlink_rcv(struct sk_buff *skb)
 
 static int __init crypto_user_init(void)
 {
+	struct netlink_kernel_cfg cfg = {
+		.input	= crypto_netlink_rcv,
+	};
+
 	crypto_nlsk = netlink_kernel_create(&init_net, NETLINK_CRYPTO,
-					    0, crypto_netlink_rcv,
-					    NULL, THIS_MODULE);
+					    THIS_MODULE, &cfg);
 	if (!crypto_nlsk)
 		return -ENOMEM;
 
diff --git a/drivers/connector/connector.c b/drivers/connector/connector.c
index 34e0e9e..116cf8d 100644
--- a/drivers/connector/connector.c
+++ b/drivers/connector/connector.c
@@ -251,15 +251,20 @@ static const struct file_operations cn_file_ops = {
 	.release = single_release
 };
 
+static struct cn_dev cdev = {
+	.input   = cn_rx_skb,
+};
+
 static int __devinit cn_init(void)
 {
 	struct cn_dev *dev = &cdev;
-
-	dev->input = cn_rx_skb;
+	struct netlink_kernel_cfg cfg = {
+		.groups	= CN_NETLINK_USERS + 0xf,
+		.input	= dev->input,
+	};
 
 	dev->nls = netlink_kernel_create(&init_net, NETLINK_CONNECTOR,
-					 CN_NETLINK_USERS + 0xf,
-					 dev->input, NULL, THIS_MODULE);
+					 THIS_MODULE, &cfg);
 	if (!dev->nls)
 		return -EIO;
 
diff --git a/drivers/infiniband/core/netlink.c b/drivers/infiniband/core/netlink.c
index 1e691dc..3ae2bfd 100644
--- a/drivers/infiniband/core/netlink.c
+++ b/drivers/infiniband/core/netlink.c
@@ -173,8 +173,11 @@ static void ibnl_rcv(struct sk_buff *skb)
 
 int __init ibnl_init(void)
 {
-	nls = netlink_kernel_create(&init_net, NETLINK_RDMA, 0, ibnl_rcv,
-				    NULL, THIS_MODULE);
+	struct netlink_kernel_cfg cfg = {
+		.input	= ibnl_rcv,
+	};
+
+	nls = netlink_kernel_create(&init_net, NETLINK_RDMA, THIS_MODULE, &cfg);
 	if (!nls) {
 		pr_warn("Failed to create netlink socket\n");
 		return -ENOMEM;
diff --git a/drivers/scsi/scsi_netlink.c b/drivers/scsi/scsi_netlink.c
index c77628a..8818dd6 100644
--- a/drivers/scsi/scsi_netlink.c
+++ b/drivers/scsi/scsi_netlink.c
@@ -486,6 +486,10 @@ void
 scsi_netlink_init(void)
 {
 	int error;
+	struct netlink_kernel_cfg cfg = {
+		.input	= scsi_nl_rcv_msg,
+		.groups	= SCSI_NL_GRP_CNT,
+	};
 
 	INIT_LIST_HEAD(&scsi_nl_drivers);
 
@@ -497,8 +501,7 @@ scsi_netlink_init(void)
 	}
 
 	scsi_nl_sock = netlink_kernel_create(&init_net, NETLINK_SCSITRANSPORT,
-				SCSI_NL_GRP_CNT, scsi_nl_rcv_msg, NULL,
-				THIS_MODULE);
+					     THIS_MODULE, &cfg);
 	if (!scsi_nl_sock) {
 		printk(KERN_ERR "%s: register of receive handler failed\n",
 				__func__);
diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c
index 1cf640e..6042954 100644
--- a/drivers/scsi/scsi_transport_iscsi.c
+++ b/drivers/scsi/scsi_transport_iscsi.c
@@ -2936,7 +2936,10 @@ EXPORT_SYMBOL_GPL(iscsi_unregister_transport);
 static __init int iscsi_transport_init(void)
 {
 	int err;
-
+	struct netlink_kernel_cfg cfg = {
+		.groups	= 1,
+		.input	= iscsi_if_rx,
+	};
 	printk(KERN_INFO "Loading iSCSI transport class v%s.\n",
 		ISCSI_TRANSPORT_VERSION);
 
@@ -2966,8 +2969,8 @@ static __init int iscsi_transport_init(void)
 	if (err)
 		goto unregister_conn_class;
 
-	nls = netlink_kernel_create(&init_net, NETLINK_ISCSI, 1, iscsi_if_rx,
-				    NULL, THIS_MODULE);
+	nls = netlink_kernel_create(&init_net, NETLINK_ISCSI,
+				    THIS_MODULE, &cfg);
 	if (!nls) {
 		err = -ENOBUFS;
 		goto unregister_session_class;
diff --git a/drivers/staging/gdm72xx/netlink_k.c b/drivers/staging/gdm72xx/netlink_k.c
index d0cb48a..d1eed1e 100644
--- a/drivers/staging/gdm72xx/netlink_k.c
+++ b/drivers/staging/gdm72xx/netlink_k.c
@@ -88,13 +88,15 @@ struct sock *netlink_init(int unit, void (*cb)(struct net_device *dev, u16 type,
 						void *msg, int len))
 {
 	struct sock *sock;
+	struct netlink_kernel_cfg cfg = {
+		.input  = netlink_rcv,
+	};
 
 #if !defined(DEFINE_MUTEX)
 	init_MUTEX(&netlink_mutex);
 #endif
 
-	sock = netlink_kernel_create(&init_net, unit, 0, netlink_rcv, NULL,
-					THIS_MODULE);
+	sock = netlink_kernel_create(&init_net, unit, 0, THIS_MODULE, &cfg);
 
 	if (sock)
 		rcv_cb = cb;
diff --git a/include/linux/netlink.h b/include/linux/netlink.h
index ed33f09..6085e49 100644
--- a/include/linux/netlink.h
+++ b/include/linux/netlink.h
@@ -174,11 +174,16 @@ struct netlink_skb_parms {
 extern void netlink_table_grab(void);
 extern void netlink_table_ungrab(void);
 
-extern struct sock *netlink_kernel_create(struct net *net,
-					  int unit,unsigned int groups,
-					  void (*input)(struct sk_buff *skb),
-					  struct mutex *cb_mutex,
-					  struct module *module);
+/* optional Netlink kernel configuration parameters */
+struct netlink_kernel_cfg {
+	unsigned int	groups;
+	void		(*input)(struct sk_buff *skb);
+	struct mutex	*cb_mutex;
+};
+
+extern struct sock *netlink_kernel_create(struct net *net, int unit,
+					  struct module *module,
+					  struct netlink_kernel_cfg *cfg);
 extern void netlink_kernel_release(struct sock *sk);
 extern int __netlink_change_ngroups(struct sock *sk, unsigned int groups);
 extern int netlink_change_ngroups(struct sock *sk, unsigned int groups);
diff --git a/kernel/audit.c b/kernel/audit.c
index 30b252a..4a3f28d 100644
--- a/kernel/audit.c
+++ b/kernel/audit.c
@@ -962,14 +962,17 @@ static void audit_receive(struct sk_buff  *skb)
 static int __init audit_init(void)
 {
 	int i;
+	struct netlink_kernel_cfg cfg = {
+		.input	= audit_receive,
+	};
 
 	if (audit_initialized == AUDIT_DISABLED)
 		return 0;
 
 	printk(KERN_INFO "audit: initializing netlink socket (%s)\n",
 	       audit_default ? "enabled" : "disabled");
-	audit_sock = netlink_kernel_create(&init_net, NETLINK_AUDIT, 0,
-					   audit_receive, NULL, THIS_MODULE);
+	audit_sock = netlink_kernel_create(&init_net, NETLINK_AUDIT,
+					   THIS_MODULE, &cfg);
 	if (!audit_sock)
 		audit_panic("cannot initialize netlink socket");
 	else
diff --git a/lib/kobject_uevent.c b/lib/kobject_uevent.c
index 1a91efa..0401d29 100644
--- a/lib/kobject_uevent.c
+++ b/lib/kobject_uevent.c
@@ -373,13 +373,16 @@ EXPORT_SYMBOL_GPL(add_uevent_var);
 static int uevent_net_init(struct net *net)
 {
 	struct uevent_sock *ue_sk;
+	struct netlink_kernel_cfg cfg = {
+		.groups	= 1,
+	};
 
 	ue_sk = kzalloc(sizeof(*ue_sk), GFP_KERNEL);
 	if (!ue_sk)
 		return -ENOMEM;
 
 	ue_sk->sk = netlink_kernel_create(net, NETLINK_KOBJECT_UEVENT,
-					  1, NULL, NULL, THIS_MODULE);
+					  THIS_MODULE, &cfg);
 	if (!ue_sk->sk) {
 		printk(KERN_ERR
 		       "kobject_uevent: unable to create netlink socket!\n");
diff --git a/net/bridge/netfilter/ebt_ulog.c b/net/bridge/netfilter/ebt_ulog.c
index 1bd1732..374bdcd 100644
--- a/net/bridge/netfilter/ebt_ulog.c
+++ b/net/bridge/netfilter/ebt_ulog.c
@@ -282,6 +282,9 @@ static int __init ebt_ulog_init(void)
 {
 	int ret;
 	int i;
+	struct netlink_kernel_cfg cfg = {
+		.groups	= EBT_ULOG_MAXNLGROUPS,
+	};
 
 	if (nlbufsiz >= 128*1024) {
 		pr_warning("Netlink buffer has to be <= 128kB,"
@@ -296,8 +299,7 @@ static int __init ebt_ulog_init(void)
 	}
 
 	ebtulognl = netlink_kernel_create(&init_net, NETLINK_NFLOG,
-					  EBT_ULOG_MAXNLGROUPS, NULL, NULL,
-					  THIS_MODULE);
+					  THIS_MODULE, &cfg);
 	if (!ebtulognl)
 		ret = -ENOMEM;
 	else if ((ret = xt_register_target(&ebt_ulog_tg_reg)) != 0)
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
index 21318d1..2db8557 100644
--- a/net/core/rtnetlink.c
+++ b/net/core/rtnetlink.c
@@ -2366,8 +2366,13 @@ static struct notifier_block rtnetlink_dev_notifier = {
 static int __net_init rtnetlink_net_init(struct net *net)
 {
 	struct sock *sk;
-	sk = netlink_kernel_create(net, NETLINK_ROUTE, RTNLGRP_MAX,
-				   rtnetlink_rcv, &rtnl_mutex, THIS_MODULE);
+	struct netlink_kernel_cfg cfg = {
+		.groups		= RTNLGRP_MAX,
+		.input		= rtnetlink_rcv,
+		.cb_mutex	= &rtnl_mutex,
+	};
+
+	sk = netlink_kernel_create(net, NETLINK_ROUTE, THIS_MODULE, &cfg);
 	if (!sk)
 		return -ENOMEM;
 	net->rtnl = sk;
diff --git a/net/core/sock_diag.c b/net/core/sock_diag.c
index 0d934ce..0929821 100644
--- a/net/core/sock_diag.c
+++ b/net/core/sock_diag.c
@@ -177,8 +177,12 @@ EXPORT_SYMBOL_GPL(sock_diag_nlsk);
 
 static int __init sock_diag_init(void)
 {
-	sock_diag_nlsk = netlink_kernel_create(&init_net, NETLINK_SOCK_DIAG, 0,
-					sock_diag_rcv, NULL, THIS_MODULE);
+	struct netlink_kernel_cfg cfg = {
+		.input	= sock_diag_rcv,
+	};
+
+	sock_diag_nlsk = netlink_kernel_create(&init_net, NETLINK_SOCK_DIAG,
+					       THIS_MODULE, &cfg);
 	return sock_diag_nlsk == NULL ? -ENOMEM : 0;
 }
 
diff --git a/net/decnet/netfilter/dn_rtmsg.c b/net/decnet/netfilter/dn_rtmsg.c
index b8f7f5b..11db0ec 100644
--- a/net/decnet/netfilter/dn_rtmsg.c
+++ b/net/decnet/netfilter/dn_rtmsg.c
@@ -125,11 +125,13 @@ static struct nf_hook_ops dnrmg_ops __read_mostly = {
 static int __init dn_rtmsg_init(void)
 {
 	int rv = 0;
+	struct netlink_kernel_cfg cfg = {
+		.groups	= DNRNG_NLGRP_MAX,
+		.input	= dnrmg_receive_user_skb,
+	};
 
 	dnrmg = netlink_kernel_create(&init_net,
-				      NETLINK_DNRTMSG, DNRNG_NLGRP_MAX,
-				      dnrmg_receive_user_skb,
-				      NULL, THIS_MODULE);
+				      NETLINK_DNRTMSG, THIS_MODULE, &cfg);
 	if (dnrmg == NULL) {
 		printk(KERN_ERR "dn_rtmsg: Cannot create netlink socket");
 		return -ENOMEM;
diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c
index 3854411..0cd820e 100644
--- a/net/ipv4/fib_frontend.c
+++ b/net/ipv4/fib_frontend.c
@@ -935,8 +935,11 @@ static void nl_fib_input(struct sk_buff *skb)
 static int __net_init nl_fib_lookup_init(struct net *net)
 {
 	struct sock *sk;
-	sk = netlink_kernel_create(net, NETLINK_FIB_LOOKUP, 0,
-				   nl_fib_input, NULL, THIS_MODULE);
+	struct netlink_kernel_cfg cfg = {
+		.input	= nl_fib_input,
+	};
+
+	sk = netlink_kernel_create(net, NETLINK_FIB_LOOKUP, THIS_MODULE, &cfg);
 	if (sk == NULL)
 		return -EAFNOSUPPORT;
 	net->ipv4.fibnl = sk;
diff --git a/net/ipv4/netfilter/ipt_ULOG.c b/net/ipv4/netfilter/ipt_ULOG.c
index 99b3f53..1109f7f 100644
--- a/net/ipv4/netfilter/ipt_ULOG.c
+++ b/net/ipv4/netfilter/ipt_ULOG.c
@@ -381,6 +381,9 @@ static struct nf_logger ipt_ulog_logger __read_mostly = {
 static int __init ulog_tg_init(void)
 {
 	int ret, i;
+	struct netlink_kernel_cfg cfg = {
+		.groups	= ULOG_MAXNLGROUPS,
+	};
 
 	pr_debug("init module\n");
 
@@ -393,9 +396,8 @@ static int __init ulog_tg_init(void)
 	for (i = 0; i < ULOG_MAXNLGROUPS; i++)
 		setup_timer(&ulog_buffers[i].timer, ulog_timer, i);
 
-	nflognl = netlink_kernel_create(&init_net,
-					NETLINK_NFLOG, ULOG_MAXNLGROUPS, NULL,
-					NULL, THIS_MODULE);
+	nflognl = netlink_kernel_create(&init_net, NETLINK_NFLOG,
+					THIS_MODULE, &cfg);
 	if (!nflognl)
 		return -ENOMEM;
 
diff --git a/net/netfilter/nfnetlink.c b/net/netfilter/nfnetlink.c
index 3e797d1..700e461 100644
--- a/net/netfilter/nfnetlink.c
+++ b/net/netfilter/nfnetlink.c
@@ -203,9 +203,12 @@ static void nfnetlink_rcv(struct sk_buff *skb)
 static int __net_init nfnetlink_net_init(struct net *net)
 {
 	struct sock *nfnl;
+	struct netlink_kernel_cfg cfg = {
+		.groups	= NFNLGRP_MAX,
+		.input	= nfnetlink_rcv,
+	};
 
-	nfnl = netlink_kernel_create(net, NETLINK_NETFILTER, NFNLGRP_MAX,
-				     nfnetlink_rcv, NULL, THIS_MODULE);
+	nfnl = netlink_kernel_create(net, NETLINK_NETFILTER, THIS_MODULE, &cfg);
 	if (!nfnl)
 		return -ENOMEM;
 	net->nfnl_stash = nfnl;
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c
index b3025a6..43a124f 100644
--- a/net/netlink/af_netlink.c
+++ b/net/netlink/af_netlink.c
@@ -1503,14 +1503,16 @@ static void netlink_data_ready(struct sock *sk, int len)
  */
 
 struct sock *
-netlink_kernel_create(struct net *net, int unit, unsigned int groups,
-		      void (*input)(struct sk_buff *skb),
-		      struct mutex *cb_mutex, struct module *module)
+netlink_kernel_create(struct net *net, int unit,
+		      struct module *module,
+		      struct netlink_kernel_cfg *cfg)
 {
 	struct socket *sock;
 	struct sock *sk;
 	struct netlink_sock *nlk;
 	struct listeners *listeners = NULL;
+	struct mutex *cb_mutex = cfg ? cfg->cb_mutex : NULL;
+	unsigned int groups;
 
 	BUG_ON(!nl_table);
 
@@ -1532,16 +1534,18 @@ netlink_kernel_create(struct net *net, int unit, unsigned int groups,
 	sk = sock->sk;
 	sk_change_net(sk, net);
 
-	if (groups < 32)
+	if (!cfg || cfg->groups < 32)
 		groups = 32;
+	else
+		groups = cfg->groups;
 
 	listeners = kzalloc(sizeof(*listeners) + NLGRPSZ(groups), GFP_KERNEL);
 	if (!listeners)
 		goto out_sock_release;
 
 	sk->sk_data_ready = netlink_data_ready;
-	if (input)
-		nlk_sk(sk)->netlink_rcv = input;
+	if (cfg && cfg->input)
+		nlk_sk(sk)->netlink_rcv = cfg->input;
 
 	if (netlink_insert(sk, net, 0))
 		goto out_sock_release;
diff --git a/net/netlink/genetlink.c b/net/netlink/genetlink.c
index 2cc7c1e..32761b5 100644
--- a/net/netlink/genetlink.c
+++ b/net/netlink/genetlink.c
@@ -915,10 +915,14 @@ static struct genl_multicast_group notify_grp = {
 
 static int __net_init genl_pernet_init(struct net *net)
 {
+	struct netlink_kernel_cfg cfg = {
+		.input		= genl_rcv,
+		.cb_mutex	= &genl_mutex,
+	};
+
 	/* we'll bump the group number right afterwards */
-	net->genl_sock = netlink_kernel_create(net, NETLINK_GENERIC, 0,
-					       genl_rcv, &genl_mutex,
-					       THIS_MODULE);
+	net->genl_sock = netlink_kernel_create(net, NETLINK_GENERIC,
+					       THIS_MODULE, &cfg);
 
 	if (!net->genl_sock && net_eq(net, &init_net))
 		panic("GENL: Cannot initialize generic netlink\n");
diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c
index 44293b3..622d049 100644
--- a/net/xfrm/xfrm_user.c
+++ b/net/xfrm/xfrm_user.c
@@ -2945,9 +2945,12 @@ static struct xfrm_mgr netlink_mgr = {
 static int __net_init xfrm_user_net_init(struct net *net)
 {
 	struct sock *nlsk;
+	struct netlink_kernel_cfg cfg = {
+		.groups	= XFRMNLGRP_MAX,
+		.input	= xfrm_netlink_rcv,
+	};
 
-	nlsk = netlink_kernel_create(net, NETLINK_XFRM, XFRMNLGRP_MAX,
-				     xfrm_netlink_rcv, NULL, THIS_MODULE);
+	nlsk = netlink_kernel_create(net, NETLINK_XFRM, THIS_MODULE, &cfg);
 	if (nlsk == NULL)
 		return -ENOMEM;
 	net->xfrm.nlsk_stash = nlsk; /* Don't set to NULL */
diff --git a/security/selinux/netlink.c b/security/selinux/netlink.c
index 8a23a35..8a77725 100644
--- a/security/selinux/netlink.c
+++ b/security/selinux/netlink.c
@@ -111,8 +111,12 @@ void selnl_notify_policyload(u32 seqno)
 
 static int __init selnl_init(void)
 {
+	struct netlink_kernel_cfg cfg = {
+		.groups	= SELNLGRP_MAX,
+	};
+
 	selnl = netlink_kernel_create(&init_net, NETLINK_SELINUX,
-				      SELNLGRP_MAX, NULL, NULL, THIS_MODULE);
+				      THIS_MODULE, &cfg);
 	if (selnl == NULL)
 		panic("SELinux:  Cannot create netlink socket.");
 	netlink_set_nonroot(NETLINK_SELINUX, NL_NONROOT_RECV);
-- 
1.7.10


^ permalink raw reply related

* [PATCH 2/2] netlink: add nlk->netlink_bind hook for module auto-loading
From: pablo @ 2012-06-29 16:15 UTC (permalink / raw)
  To: netfilter-devel; +Cc: davem, netdev
In-Reply-To: <1340986522-3442-1-git-send-email-pablo@netfilter.org>

From: Pablo Neira Ayuso <pablo@netfilter.org>

This patch adds a hook in the binding path of netlink.

This is used by ctnetlink to allow module autoloading for the case
in which one user executes:

 conntrack -E

So far, this resulted in nfnetlink loaded, but not
nf_conntrack_netlink.

I have received in the past many complains on this behaviour.

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 include/linux/netlink.h   |    1 +
 net/netfilter/nfnetlink.c |   29 +++++++++++++++++++++++++++++
 net/netlink/af_netlink.c  |   19 +++++++++++++++++++
 3 files changed, 49 insertions(+)

diff --git a/include/linux/netlink.h b/include/linux/netlink.h
index 6085e49..f74dd13 100644
--- a/include/linux/netlink.h
+++ b/include/linux/netlink.h
@@ -179,6 +179,7 @@ struct netlink_kernel_cfg {
 	unsigned int	groups;
 	void		(*input)(struct sk_buff *skb);
 	struct mutex	*cb_mutex;
+	void		(*bind)(int group);
 };
 
 extern struct sock *netlink_kernel_create(struct net *net, int unit,
diff --git a/net/netfilter/nfnetlink.c b/net/netfilter/nfnetlink.c
index 700e461..5a2132b 100644
--- a/net/netfilter/nfnetlink.c
+++ b/net/netfilter/nfnetlink.c
@@ -39,6 +39,15 @@ static char __initdata nfversion[] = "0.30";
 static const struct nfnetlink_subsystem __rcu *subsys_table[NFNL_SUBSYS_COUNT];
 static DEFINE_MUTEX(nfnl_mutex);
 
+static const int nfnl_group2type[NFNLGRP_MAX+1] = {
+	[NFNLGRP_CONNTRACK_NEW]		= NFNL_SUBSYS_CTNETLINK,
+	[NFNLGRP_CONNTRACK_UPDATE]	= NFNL_SUBSYS_CTNETLINK,
+	[NFNLGRP_CONNTRACK_DESTROY]	= NFNL_SUBSYS_CTNETLINK,
+	[NFNLGRP_CONNTRACK_EXP_NEW]	= NFNL_SUBSYS_CTNETLINK_EXP,
+	[NFNLGRP_CONNTRACK_EXP_UPDATE]	= NFNL_SUBSYS_CTNETLINK_EXP,
+	[NFNLGRP_CONNTRACK_EXP_DESTROY] = NFNL_SUBSYS_CTNETLINK_EXP,
+};
+
 void nfnl_lock(void)
 {
 	mutex_lock(&nfnl_mutex);
@@ -200,12 +209,32 @@ static void nfnetlink_rcv(struct sk_buff *skb)
 	netlink_rcv_skb(skb, &nfnetlink_rcv_msg);
 }
 
+#ifdef CONFIG_MODULES
+static void nfnetlink_bind(int group)
+{
+	const struct nfnetlink_subsystem *ss;
+	int type = nfnl_group2type[group];
+
+	rcu_read_lock();
+	ss = nfnetlink_get_subsys(type);
+	if (!ss) {
+		rcu_read_unlock();
+		request_module("nfnetlink-subsys-%d", type);
+		return;
+	}
+	rcu_read_unlock();
+}
+#endif
+
 static int __net_init nfnetlink_net_init(struct net *net)
 {
 	struct sock *nfnl;
 	struct netlink_kernel_cfg cfg = {
 		.groups	= NFNLGRP_MAX,
 		.input	= nfnetlink_rcv,
+#ifdef CONFIG_MODULES
+		.bind	= nfnetlink_bind,
+#endif
 	};
 
 	nfnl = netlink_kernel_create(net, NETLINK_NETFILTER, THIS_MODULE, &cfg);
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c
index 43a124f..5463969 100644
--- a/net/netlink/af_netlink.c
+++ b/net/netlink/af_netlink.c
@@ -80,6 +80,7 @@ struct netlink_sock {
 	struct mutex		*cb_mutex;
 	struct mutex		cb_def_mutex;
 	void			(*netlink_rcv)(struct sk_buff *skb);
+	void			(*netlink_bind)(int group);
 	struct module		*module;
 };
 
@@ -124,6 +125,7 @@ struct netlink_table {
 	unsigned int		groups;
 	struct mutex		*cb_mutex;
 	struct module		*module;
+	void			(*bind)(int group);
 	int			registered;
 };
 
@@ -444,6 +446,7 @@ static int netlink_create(struct net *net, struct socket *sock, int protocol,
 	struct module *module = NULL;
 	struct mutex *cb_mutex;
 	struct netlink_sock *nlk;
+	void (*bind)(int group);
 	int err = 0;
 
 	sock->state = SS_UNCONNECTED;
@@ -468,6 +471,7 @@ static int netlink_create(struct net *net, struct socket *sock, int protocol,
 	else
 		err = -EPROTONOSUPPORT;
 	cb_mutex = nl_table[protocol].cb_mutex;
+	bind = nl_table[protocol].bind;
 	netlink_unlock_table();
 
 	if (err < 0)
@@ -483,6 +487,7 @@ static int netlink_create(struct net *net, struct socket *sock, int protocol,
 
 	nlk = nlk_sk(sock->sk);
 	nlk->module = module;
+	nlk->netlink_bind = bind;
 out:
 	return err;
 
@@ -683,6 +688,15 @@ static int netlink_bind(struct socket *sock, struct sockaddr *addr,
 	netlink_update_listeners(sk);
 	netlink_table_ungrab();
 
+	if (nlk->netlink_bind && nlk->groups[0]) {
+		int i;
+
+		for (i=0; i<nlk->ngroups; i++) {
+			if (test_bit(i, nlk->groups))
+				nlk->netlink_bind(i);
+		}
+	}
+
 	return 0;
 }
 
@@ -1239,6 +1253,10 @@ static int netlink_setsockopt(struct socket *sock, int level, int optname,
 		netlink_update_socket_mc(nlk, val,
 					 optname == NETLINK_ADD_MEMBERSHIP);
 		netlink_table_ungrab();
+
+		if (nlk->netlink_bind)
+			nlk->netlink_bind(val);
+
 		err = 0;
 		break;
 	}
@@ -1559,6 +1577,7 @@ netlink_kernel_create(struct net *net, int unit,
 		rcu_assign_pointer(nl_table[unit].listeners, listeners);
 		nl_table[unit].cb_mutex = cb_mutex;
 		nl_table[unit].module = module;
+		nl_table[unit].bind = cfg ? cfg->bind : NULL;
 		nl_table[unit].registered = 1;
 	} else {
 		kfree(listeners);
-- 
1.7.10

^ permalink raw reply related

* Re: BUG: NULL pointer in ctnetlink_conntrack_event
From: Pablo Neira Ayuso @ 2012-06-29 16:29 UTC (permalink / raw)
  To: Hans Schillstrom; +Cc: netdev, netfilter-devel
In-Reply-To: <mdaatbj.9730f14767bb2419ed11e14ac50382b8@obelix.schillstrom.com>

[-- Attachment #1: Type: text/plain, Size: 6403 bytes --]

On Fri, Jun 29, 2012 at 02:29:37PM +0200, Hans Schillstrom wrote:
> Hello,
> 
> There is a "hard to find" problem in ctnetlink_conntrack_event() when calling
> netlink_has_listeners() net->nfnl is NULL.
> 
> The rcu stuff seems to be right at a first look but who knows...
> 
> The line below fix the problem, but that is not the root cause.
> 
>  int nfnetlink_has_listeners(struct net *net, unsigned int group)
>  {
> -       return netlink_has_listeners(net->nfnl, group);
> +       return net->nfnl ? netlink_has_listeners(net->nfnl, group) : 0 ;
>  }
> 
> Yes it is a 3.0.26 kernel but this patch is applied
> netfilter: nf_conntrack: make event callback registration per-netns

I think this patch above is missing some rcu_access_pointer usage.

Please, see patch attached.

> It happens when adding a number of containers with does a "nfct_query(h, NFCT_Q_CREATE, ct);"
> and most likely one namespace shuts down.
> 
> Any idea why the timer is running at this point ?
> 
> 
> BUG: unable to handle kernel NULL pointer dereference at 000000000000027c
> IP: [<ffffffff813615db>] netlink_has_listeners+0xb/0x60
> PGD 0
> Oops: 0000 [#3] PREEMPT SMP
> CPU 0
> Modules linked in: ip6table_raw(N) xt_NOTRACK(N) iptable_raw(N) ipt_REJECT(N) xt_sctp(N) xt_multiport(N) xt_connmark(N) xt_mark(N) xt_conntrack(N) ip6table_mangle(N) ip_vs(N) nf_conntrack_netlink(N) nfnetlink(N) ip6_tunnel(N) tunnel6(N) macvlan(N) xt_HMARK(N) ipv6_find_hdr(N) iptable_mangle(N) nf_conntrack_ipv6(N) nf_defrag_ipv6(N) ip6t_LOG(N) ip6table_filter(N) ip6_tables(N) nf_conntrack_ipv4(N) nf_defrag_ipv4(N) xt_state(N) xt_tcpudp(N) xt_u32(N) xt_comment(N) xt_length(N) xt_hashlimit(N) ipt_LOG(N) xt_limit(N) iptable_filter(N) ip_tables(N) x_tables(N) nf_conntrack_ftp(N) nf_conntrack_tftp(N) nf_conntrack(N) mptsas(N) mptscsih(N) mptbase(N) sg(N) scsi_transport_sas(N) i2c_i801(N) i2c_core(N) button(N) pcspkr(N) ahci(N) libahci(N) processor(N) serio_raw(N) thermal_sys(N) hwmon(N) iTCO_wdt(N) iTCO_vendor_support(N) libata(N) ioatdma(N) ixgbe(N) mdio(N) nfs(N) lockd(N) fscache(N) auth_rpcgss(N) nfs_acl(N) sunrpc(N) af_packet(N) ipv6(N) ipv6_lib(N) bonding(N) e1000e(N) igb(N) dca(N) mii(N) 8021q(N) garp(N) st
> p(N) llc(N) softdog(N) xfs(N) exportfs(N) sd_mod(N) crc_t10dif(N) usb_storage(N) scsi_mod(N) ehci_hcd(N) uhci_hcd(N) usbcore(N) usb_common(N)
> Supported: Yes
> 
> Pid: 0, comm: swapper Tainted: G      D    N  3.0.26-0.2-default
> RIP: 0010:[<ffffffff813615db>]  [<ffffffff813615db>] netlink_has_listeners+0xb/0x60
> RSP: 0018:ffff88063f203da0  EFLAGS: 00010286
> RAX: ffff88063f203e30 RBX: 0000000000000000 RCX: ffffffffa04c60f0
> RDX: 0000000000000004 RSI: 0000000000000003 RDI: 0000000000000000
> RBP: 0000000000000003 R08: 0000000000000000 R09: ffff88063f2114a0
> R10: 0000000000000000 R11: ffffffff8101e760 R12: ffff8805e2a45788
> R13: 0000000000000000 R14: 0000000000000002 R15: 0000000000000004
> FS:  0000000000000000(0000) GS:ffff88063f200000(0000) knlGS:0000000000000000
> CS:  0010 DS: 0000 ES: 0000 CR0: 000000008005003b
> CR2: 000000000000027c CR3: 0000000001a03000 CR4: 00000000000006f0
> DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
> DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400
> Process swapper (pid: 0, threadinfo ffffffff81a00000, task ffffffff81a0b020)
> Stack:
>  0000000000000000 0000000000000000 ffff8805e2a45800 ffffffffa04c453e
>  ffff88063f203e30 0000000400000001 ffff8805e24e6c80 0000000300000000
>  0000000000000000 ffff880610044000 ffff880610044800 ffff8805e2a45788
> Call Trace:
>  [<ffffffffa04c453e>] ctnetlink_conntrack_event+0x51e/0x570 [nf_conntrack_netlink]
>  [<ffffffffa042a27b>] death_by_timeout+0x12b/0x190 [nf_conntrack]
>  [<ffffffff810608ec>] run_timer_softirq+0x14c/0x270
>  [<ffffffff81059d25>] __do_softirq+0xa5/0x180
>  [<ffffffff813ff43c>] call_softirq+0x1c/0x30
>  [<ffffffff810043f5>] do_softirq+0x65/0xa0
>  [<ffffffff81059b15>] irq_exit+0xc5/0x100
>  [<ffffffff8101f5a9>] smp_apic_timer_interrupt+0x69/0xa0
>  [<ffffffff813febf3>] apic_timer_interrupt+0x13/0x20
>  [<ffffffffa0230806>] acpi_idle_enter_bm+0x255/0x28f [processor]
>  [<ffffffff813179e2>] cpuidle_idle_call+0xd2/0x120
>  [<ffffffff810019f3>] cpu_idle+0x63/0xd0
>  [<ffffffff81bf0f65>] start_kernel+0x3e4/0x4bf
>  [<ffffffff81bf03c3>] x86_64_start_kernel+0x114/0x12f
> Code: ff 66 66 66 66 66 66 2e 0f 1f 84 00 00 00 00 00 e9 cb c5 fc ff 66 66 2e 0f 1f 84 00 00 00 00 00 55 89 f5 53 48 89 fb 48 83 ec 08 <f6> 87 7c 02 00 00 01 74 41 e8 47 50 d5 ff 0f b6 83 21 01 00 00
> RIP  [<ffffffff813615db>] netlink_has_listeners+0xb/0x60
>  RSP <ffff88063f203da0>
> CR2: 000000000000027c
> ---[ end trace a057af0b3004c67a ]---
> Kernel panic - not syncing: Fatal exception in interrupt
> Pid: 0, comm: swapper Tainted: G      D    N  3.0.26-0.2-default #1
> Call Trace:
>  [<ffffffff81004672>] dump_trace+0x82/0x380
>  [<ffffffff813f4fa2>] dump_stack+0x69/0x6f
>  [<ffffffff813f5050>] panic+0xa8/0x20c
>  [<ffffffff813f9b21>] oops_end+0xe1/0xf0
>  [<ffffffff81030e50>] no_context+0x100/0x270
>  [<ffffffff81031135>] __bad_area_nosemaphore+0x175/0x220
>  [<ffffffff813fbb36>] do_page_fault+0x3a6/0x590
>  [<ffffffff813f8d15>] page_fault+0x25/0x30
>  [<ffffffff813615db>] netlink_has_listeners+0xb/0x60
>  [<ffffffffa04c453e>] ctnetlink_conntrack_event+0x51e/0x570 [nf_conntrack_netlink]
>  [<ffffffffa042a27b>] death_by_timeout+0x12b/0x190 [nf_conntrack]
>  [<ffffffff810608ec>] run_timer_softirq+0x14c/0x270
>  [<ffffffff81059d25>] __do_softirq+0xa5/0x180
>  [<ffffffff813ff43c>] call_softirq+0x1c/0x30
>  [<ffffffff810043f5>] do_softirq+0x65/0xa0
>  [<ffffffff81059b15>] irq_exit+0xc5/0x100
>  [<ffffffff8101f5a9>] smp_apic_timer_interrupt+0x69/0xa0
>  [<ffffffff813febf3>] apic_timer_interrupt+0x13/0x20
>  [<ffffffffa0230806>] acpi_idle_enter_bm+0x255/0x28f [processor]
>  [<ffffffff813179e2>] cpuidle_idle_call+0xd2/0x120
>  [<ffffffff810019f3>] cpu_idle+0x63/0xd0
>  [<ffffffff81bf0f65>] start_kernel+0x3e4/0x4bf
>  [<ffffffff81bf03c3>] x86_64_start_kernel+0x114/0x12f
> Rebooting in 1 seconds..
> --
> Regards 
> Hans Schillstrom
> 
> 
> 
> --
> To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

[-- Attachment #2: missing-rcu_access_pointer.patch --]
[-- Type: text/x-diff, Size: 543 bytes --]

diff --git a/include/net/netfilter/nf_conntrack_ecache.h b/include/net/netfilter/nf_conntrack_ecache.h
index a88fb69..e1ce104 100644
--- a/include/net/netfilter/nf_conntrack_ecache.h
+++ b/include/net/netfilter/nf_conntrack_ecache.h
@@ -78,7 +78,7 @@ nf_conntrack_event_cache(enum ip_conntrack_events event, struct nf_conn *ct)
 	struct net *net = nf_ct_net(ct);
 	struct nf_conntrack_ecache *e;
 
-	if (net->ct.nf_conntrack_event_cb == NULL)
+	if (!rcu_access_pointer(net->ct.nf_conntrack_event_cb))
 		return;
 
 	e = nf_ct_ecache_find(ct);

^ permalink raw reply related

* [PATCH v3] sctp: be more restrictive in transport selection on bundled sacks
From: Neil Horman @ 2012-06-29 16:34 UTC (permalink / raw)
  To: netdev; +Cc: Neil Horman, Vlad Yaseivch, David S. Miller
In-Reply-To: <1340742704-2192-1-git-send-email-nhorman@tuxdriver.com>

It was noticed recently that when we send data on a transport, its possible that
we might bundle a sack that arrived on a different transport.  While this isn't
a major problem, it does go against the SHOULD requirement in section 6.4 of RFC
2960:

 An endpoint SHOULD transmit reply chunks (e.g., SACK, HEARTBEAT ACK,
   etc.) to the same destination transport address from which it
   received the DATA or control chunk to which it is replying.  This
   rule should also be followed if the endpoint is bundling DATA chunks
   together with the reply chunk.

This patch seeks to correct that.  It restricts the bundling of sack operations
to only those transports which have moved the ctsn of the association forward
since the last sack.  By doing this we guarantee that we only bundle outbound
saks on a transport that has received a chunk since the last sack.  This brings
us into stricter compliance with the RFC.

Vlad had initially suggested that we strictly allow only sack bundling on the
transport that last moved the ctsn forward.  While this makes sense, I was
concerned that doing so prevented us from bundling in the case where we had
received chunks that moved the ctsn on multiple transports.  In those cases, the
RFC allows us to select any of the transports having received chunks to bundle
the sack on.  so I've modified the approach to allow for that, by adding a state
variable to each transport that tracks weather it has moved the ctsn since the
last sack.  This I think keeps our behavior (and performance), close enough to
our current profile that I think we can do this without a sysctl knob to
enable/disable it.

Signed-off-by: Neil Horman <nhorman@tuxdriver.com>
CC: Vlad Yaseivch <vyasevich@gmail.com>
CC: David S. Miller <davem@davemloft.net>
Reported-by: Michele Baldessari <michele@redhat.com>
Reported-by: sorin serban <sserban@redhat.com>

---
Change Notes:
V2)
	* Removed unused variable as per Dave M. Request
	* Delayed rwnd adjustment until we are sure we will sack (Vlad Y.)
V3)
	* Switched test to use pkt->transport rather than chunk->transport
	* Modified detection of sacka-able transport.  Instead of just setting
	  and clearning a flag, we now mark each transport and association with
	  a sack generation tag.  We increment the associations generation on
	  every sack, and assign that generation tag to every transport that
	  updates the ctsn.  This prevents us from having to iterate over a for
	  loop on every sack, which is much more scalable.
---
 include/net/sctp/structs.h |    4 ++++
 include/net/sctp/tsnmap.h  |    3 ++-
 net/sctp/associola.c       |    1 +
 net/sctp/output.c          |    9 +++++++--
 net/sctp/sm_make_chunk.c   |   10 ++++++++++
 net/sctp/sm_sideeffect.c   |    2 +-
 net/sctp/transport.c       |    2 ++
 net/sctp/tsnmap.c          |    6 +++++-
 net/sctp/ulpevent.c        |    3 ++-
 net/sctp/ulpqueue.c        |    2 +-
 10 files changed, 35 insertions(+), 7 deletions(-)

diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h
index e4652fe..fecdf31 100644
--- a/include/net/sctp/structs.h
+++ b/include/net/sctp/structs.h
@@ -912,6 +912,9 @@ struct sctp_transport {
 		/* Is this structure kfree()able? */
 		malloced:1;
 
+	/* Has this transport moved the ctsn since we last sacked */
+	__u32 sack_generation;
+
 	struct flowi fl;
 
 	/* This is the peer's IP address and port. */
@@ -1584,6 +1587,7 @@ struct sctp_association {
 		 */
 		__u8    sack_needed;     /* Do we need to sack the peer? */
 		__u32	sack_cnt;
+		__u32	sack_generation;
 
 		/* These are capabilities which our peer advertised.  */
 		__u8	ecn_capable:1,	    /* Can peer do ECN? */
diff --git a/include/net/sctp/tsnmap.h b/include/net/sctp/tsnmap.h
index e7728bc..2c5d2b4 100644
--- a/include/net/sctp/tsnmap.h
+++ b/include/net/sctp/tsnmap.h
@@ -117,7 +117,8 @@ void sctp_tsnmap_free(struct sctp_tsnmap *map);
 int sctp_tsnmap_check(const struct sctp_tsnmap *, __u32 tsn);
 
 /* Mark this TSN as seen.  */
-int sctp_tsnmap_mark(struct sctp_tsnmap *, __u32 tsn);
+int sctp_tsnmap_mark(struct sctp_tsnmap *, __u32 tsn,
+		     struct sctp_transport *trans);
 
 /* Mark this TSN and all lower as seen. */
 void sctp_tsnmap_skip(struct sctp_tsnmap *map, __u32 tsn);
diff --git a/net/sctp/associola.c b/net/sctp/associola.c
index 5bc9ab1..6c66adb 100644
--- a/net/sctp/associola.c
+++ b/net/sctp/associola.c
@@ -271,6 +271,7 @@ static struct sctp_association *sctp_association_init(struct sctp_association *a
 	 */
 	asoc->peer.sack_needed = 1;
 	asoc->peer.sack_cnt = 0;
+	asoc->peer.sack_generation=0;
 
 	/* Assume that the peer will tell us if he recognizes ASCONF
 	 * as part of INIT exchange.
diff --git a/net/sctp/output.c b/net/sctp/output.c
index f1b7d4b..0de6cd5 100644
--- a/net/sctp/output.c
+++ b/net/sctp/output.c
@@ -240,14 +240,19 @@ static sctp_xmit_t sctp_packet_bundle_sack(struct sctp_packet *pkt,
 	 */
 	if (sctp_chunk_is_data(chunk) && !pkt->has_sack &&
 	    !pkt->has_cookie_echo) {
-		struct sctp_association *asoc;
 		struct timer_list *timer;
-		asoc = pkt->transport->asoc;
+		struct sctp_association *asoc = pkt->transport->asoc;
+
 		timer = &asoc->timers[SCTP_EVENT_TIMEOUT_SACK];
 
 		/* If the SACK timer is running, we have a pending SACK */
 		if (timer_pending(timer)) {
 			struct sctp_chunk *sack;
+
+			if (pkt->transport->sack_generation !=
+			    pkt->transport->asoc->peer.sack_generation)
+				return retval;
+
 			asoc->a_rwnd = asoc->rwnd;
 			sack = sctp_make_sack(asoc);
 			if (sack) {
diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c
index a85eeeb..ffa2a8e 100644
--- a/net/sctp/sm_make_chunk.c
+++ b/net/sctp/sm_make_chunk.c
@@ -736,6 +736,7 @@ struct sctp_chunk *sctp_make_sack(const struct sctp_association *asoc)
 	__u16 num_gabs, num_dup_tsns;
 	struct sctp_tsnmap *map = (struct sctp_tsnmap *)&asoc->peer.tsn_map;
 	struct sctp_gap_ack_block gabs[SCTP_MAX_GABS];
+	struct sctp_transport *trans;
 
 	memset(gabs, 0, sizeof(gabs));
 	ctsn = sctp_tsnmap_get_ctsn(map);
@@ -805,6 +806,15 @@ struct sctp_chunk *sctp_make_sack(const struct sctp_association *asoc)
 		sctp_addto_chunk(retval, sizeof(__u32) * num_dup_tsns,
 				 sctp_tsnmap_get_dups(map));
 
+	/*
+	 * Once we have a sack generated, clear the moved_tsn information
+	 * from all the transports
+	 */
+	if (!asoc->peer.sack_generation)
+		list_for_each_entry(trans, &asoc->peer.transport_addr_list,
+				    transports)
+			trans->sack_generation = UINT_MAX;
+	((struct sctp_association *)asoc)->peer.sack_generation++;
 nodata:
 	return retval;
 }
diff --git a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c
index c96d1a8..8716da1 100644
--- a/net/sctp/sm_sideeffect.c
+++ b/net/sctp/sm_sideeffect.c
@@ -1268,7 +1268,7 @@ static int sctp_cmd_interpreter(sctp_event_t event_type,
 		case SCTP_CMD_REPORT_TSN:
 			/* Record the arrival of a TSN.  */
 			error = sctp_tsnmap_mark(&asoc->peer.tsn_map,
-						 cmd->obj.u32);
+						 cmd->obj.u32, NULL);
 			break;
 
 		case SCTP_CMD_REPORT_FWDTSN:
diff --git a/net/sctp/transport.c b/net/sctp/transport.c
index b026ba0..1dcceb6 100644
--- a/net/sctp/transport.c
+++ b/net/sctp/transport.c
@@ -68,6 +68,8 @@ static struct sctp_transport *sctp_transport_init(struct sctp_transport *peer,
 	peer->af_specific = sctp_get_af_specific(addr->sa.sa_family);
 	memset(&peer->saddr, 0, sizeof(union sctp_addr));
 
+	peer->sack_generation = 0;
+
 	/* From 6.3.1 RTO Calculation:
 	 *
 	 * C1) Until an RTT measurement has been made for a packet sent to the
diff --git a/net/sctp/tsnmap.c b/net/sctp/tsnmap.c
index f1e40ceb..b5fb7c4 100644
--- a/net/sctp/tsnmap.c
+++ b/net/sctp/tsnmap.c
@@ -114,7 +114,8 @@ int sctp_tsnmap_check(const struct sctp_tsnmap *map, __u32 tsn)
 
 
 /* Mark this TSN as seen.  */
-int sctp_tsnmap_mark(struct sctp_tsnmap *map, __u32 tsn)
+int sctp_tsnmap_mark(struct sctp_tsnmap *map, __u32 tsn,
+		     struct sctp_transport *trans)
 {
 	u16 gap;
 
@@ -133,6 +134,9 @@ int sctp_tsnmap_mark(struct sctp_tsnmap *map, __u32 tsn)
 		 */
 		map->max_tsn_seen++;
 		map->cumulative_tsn_ack_point++;
+		if (trans)
+			trans->sack_generation =
+				trans->asoc->peer.sack_generation;
 		map->base_tsn++;
 	} else {
 		/* Either we already have a gap, or about to record a gap, so
diff --git a/net/sctp/ulpevent.c b/net/sctp/ulpevent.c
index 8a84017..33d8947 100644
--- a/net/sctp/ulpevent.c
+++ b/net/sctp/ulpevent.c
@@ -715,7 +715,8 @@ struct sctp_ulpevent *sctp_ulpevent_make_rcvmsg(struct sctp_association *asoc,
 	 * can mark it as received so the tsn_map is updated correctly.
 	 */
 	if (sctp_tsnmap_mark(&asoc->peer.tsn_map,
-			     ntohl(chunk->subh.data_hdr->tsn)))
+			     ntohl(chunk->subh.data_hdr->tsn),
+			     chunk->transport))
 		goto fail_mark;
 
 	/* First calculate the padding, so we don't inadvertently
diff --git a/net/sctp/ulpqueue.c b/net/sctp/ulpqueue.c
index f2d1de7..f5a6a4f 100644
--- a/net/sctp/ulpqueue.c
+++ b/net/sctp/ulpqueue.c
@@ -1051,7 +1051,7 @@ void sctp_ulpq_renege(struct sctp_ulpq *ulpq, struct sctp_chunk *chunk,
 	if (chunk && (freed >= needed)) {
 		__u32 tsn;
 		tsn = ntohl(chunk->subh.data_hdr->tsn);
-		sctp_tsnmap_mark(&asoc->peer.tsn_map, tsn);
+		sctp_tsnmap_mark(&asoc->peer.tsn_map, tsn, chunk->transport);
 		sctp_ulpq_tail_data(ulpq, chunk, gfp);
 
 		sctp_ulpq_partial_delivery(ulpq, chunk, gfp);
-- 
1.7.7.6

^ permalink raw reply related

* AF_BUS socket address family
From: Vincent Sanders @ 2012-06-29 16:45 UTC (permalink / raw)
  To: netdev, linux-kernel, David S. Miller


This series adds the bus address family (AF_BUS) it is against
net-next as of yesterday.
 
AF_BUS is a message oriented inter process communication system. 

The principle features are:

 - Reliable datagram based communication (all sockets are of type
   SOCK_SEQPACKET)

 - Multicast message delivery (one to many, unicast as a subset)

 - Strict ordering (messages are delivered to every client in the same order)

 - Ability to pass file descriptors

 - Ability to pass credentials

The basic concept is to provide a virtual bus on which multiple
processes can communicate and policy is imposed by a "bus master".

Introduction
------------

AF_BUS is based upon AF_UNIX but extended for multicast operation and
removes stream operation, responding to extensive feedback on previous
approaches we have made the implementation as isolated as
possible. There are opportunities in the future to integrate the
socket garbage collector with that of the unix socket implementation.

The impetus for creating this IPC mechanism is to replace the
underlying transport for D-Bus. The D-Bus system currently emulates this
IPC mechanism using AF_UNIX sockets in userspace and has numerous
undesirable behaviours. D-Bus is now widely deployed in many areas and
has become a de-facto IPC standard. Using this IPC mechanism as a
transport gives a significant (100% or more) improvement to throughput
with comparable improvement to latency.

This work was undertaken by Collabora for the GENIVI Alliance and we
are committed to responding to feedback promptly and intend to continue
to support this feature into the future.

Operation
---------

A bus is created by processes connecting on an AF_BUS socket. The
"bus master" binds itself instead of connecting to the NULL address.

The socket address is made up of a path component and a numeric
component. The path component is either a pathname or an abstract
socket similar to a unix socket. The numeric component is used to
uniquely identify each connection to the bus. Thus the path identifies
a specific bus and the numeric component the attachment to that bus.

The numeric component of the address is divided into two fixed parts a
prefix to identify multicast groups and a suffix which identifies the
attachment. The kernel allocates a single address in prefix 0 to each
socket upon connection.

Connections are initially limited to communicating with address the
bus master (address 0) . The bus master is responsible for making all
policy decisions around manipulating other attachments including
building multicast groups. 

It is expected that connecting clients use protocol specific messages
to communicate with the bus master to negotiate differing
configurations although a bus master might implement a fixed
behaviour.

AF_BUS itself is protocol agnostic and implements the configured
policy between attachments which allows for a bus master to leave a
bus and communication between clients to continue.

Some test code has been written [1] which demonstrates the usage of
AF_BUS.

Use with BUS_PROTO_DBUS
-----------------------

The initial aim of AF_BUS is to provide a IPC mechanism suitable for
use to provide the underlying transport for D-Bus. 

A socket created using BUS_PROTO_DBUS indicates that the messages
passed will be in the D-Bus format. The userspace libraries have been
updated to use this transport with an updated D-Bus daemon [2] as a bus
master.

The D-Bus protocol allows for multicast groups to be filtered depending
on message contents. These filters are configured by the bus master
but need to be enforced on message delivery. 

We have simply used the standard kernel netfilter mechanism to achieve
this. This is used to filter delivery to clients that may be part of a
multicast group where they are not receiving all messages according to
policy. If a client wishes to further filter its input provision has
been made to allow them to use BPF.

The kernel based IPC has several benefits for D-Bus over the userspace
emulation:

 - Context switching between userspace processes is reduced.
 - Message data copying is reduced.
 - System call overheads are reduced.
 - The userspace D-Bus daemon was subject to resource starvation,
   client contention and priority inversion.
 - Latency is reduced
 - Throughput is increased.

The tools for testing these assertions are available [3] and
consistently show a doubling in throughput and better than halving of
latency.

[1] http://cgit.collabora.com/git/user/javier/check-unix-multicast.git/log/?h=af-bus
[2] http://cgit.collabora.com/git/user/rodrigo/dbus.git/

[3] git://github.com/kanchev/dbus-ping.git
    https://github.com/kanchev/dbus-ping/blob/master/dbus-genivi-benchmarking.sh

^ permalink raw reply

* [PATCH net-next 01/15] net: bus: Add AF_BUS socket address family
From: Vincent Sanders @ 2012-06-29 16:45 UTC (permalink / raw)
  To: netdev, linux-kernel, David S. Miller
  Cc: Javier Martinez Canillas, Vincent Sanders
In-Reply-To: <1340988354-26981-1-git-send-email-vincent.sanders@collabora.co.uk>

From: Javier Martinez Canillas <javier.martinez@collabora.co.uk>

This adds AF_BUS to the socket headers and net core.

AF_BUS is a message oriented inter process communication system
implemented as asocket address family. The principle features are:

 - Reliable datagram based communication (all sockets are of type
   SOCK_SEQPACKET)
 - Multicast message delivery (one to many, unicast as a subset)
 - Strict ordering (messages are delivered to every client in the same order)
 - Ability to pass file descriptors
 - Ability to pass credentials

Signed-off-by: Javier Martinez Canillas <javier.martinez@collabora.co.uk>
Signed-off-by: Vincent Sanders <vincent.sanders@collabora.co.uk>
---
 include/linux/socket.h |    5 ++++-
 net/core/sock.c        |    6 +++---
 2 files changed, 7 insertions(+), 4 deletions(-)

diff --git a/include/linux/socket.h b/include/linux/socket.h
index 25d6322..d244e69 100644
--- a/include/linux/socket.h
+++ b/include/linux/socket.h
@@ -195,7 +195,8 @@ struct ucred {
 #define AF_CAIF		37	/* CAIF sockets			*/
 #define AF_ALG		38	/* Algorithm sockets		*/
 #define AF_NFC		39	/* NFC sockets			*/
-#define AF_MAX		40	/* For now.. */
+#define AF_BUS		40	/* BUS sockets			*/
+#define AF_MAX		41	/* For now.. */
 
 /* Protocol families, same as address families. */
 #define PF_UNSPEC	AF_UNSPEC
@@ -238,6 +239,7 @@ struct ucred {
 #define PF_CAIF		AF_CAIF
 #define PF_ALG		AF_ALG
 #define PF_NFC		AF_NFC
+#define PF_BUS		AF_BUS
 #define PF_MAX		AF_MAX
 
 /* Maximum queue length specifiable by listen.  */
@@ -312,6 +314,7 @@ struct ucred {
 #define SOL_IUCV	277
 #define SOL_CAIF	278
 #define SOL_ALG		279
+#define SOL_BUS		280
 
 /* IPX options */
 #define IPX_TYPE	1
diff --git a/net/core/sock.c b/net/core/sock.c
index 929bdcc..b9c5fc8 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -208,7 +208,7 @@ static const char *const af_family_key_strings[AF_MAX+1] = {
   "sk_lock-AF_TIPC"  , "sk_lock-AF_BLUETOOTH", "sk_lock-IUCV"        ,
   "sk_lock-AF_RXRPC" , "sk_lock-AF_ISDN"     , "sk_lock-AF_PHONET"   ,
   "sk_lock-AF_IEEE802154", "sk_lock-AF_CAIF" , "sk_lock-AF_ALG"      ,
-  "sk_lock-AF_NFC"   , "sk_lock-AF_MAX"
+  "sk_lock-AF_NFC"   , "sk_lock-AF_BUS"     , "sk_lock-AF_MAX"
 };
 static const char *const af_family_slock_key_strings[AF_MAX+1] = {
   "slock-AF_UNSPEC", "slock-AF_UNIX"     , "slock-AF_INET"     ,
@@ -224,7 +224,7 @@ static const char *const af_family_slock_key_strings[AF_MAX+1] = {
   "slock-AF_TIPC"  , "slock-AF_BLUETOOTH", "slock-AF_IUCV"     ,
   "slock-AF_RXRPC" , "slock-AF_ISDN"     , "slock-AF_PHONET"   ,
   "slock-AF_IEEE802154", "slock-AF_CAIF" , "slock-AF_ALG"      ,
-  "slock-AF_NFC"   , "slock-AF_MAX"
+  "slock-AF_NFC"   , "slock-AF_BUS"     , "slock-AF_MAX"
 };
 static const char *const af_family_clock_key_strings[AF_MAX+1] = {
   "clock-AF_UNSPEC", "clock-AF_UNIX"     , "clock-AF_INET"     ,
@@ -240,7 +240,7 @@ static const char *const af_family_clock_key_strings[AF_MAX+1] = {
   "clock-AF_TIPC"  , "clock-AF_BLUETOOTH", "clock-AF_IUCV"     ,
   "clock-AF_RXRPC" , "clock-AF_ISDN"     , "clock-AF_PHONET"   ,
   "clock-AF_IEEE802154", "clock-AF_CAIF" , "clock-AF_ALG"      ,
-  "clock-AF_NFC"   , "clock-AF_MAX"
+  "clock-AF_NFC"   , "clock-AF_BUS"     , "clock-AF_MAX"
 };
 
 /*
-- 
1.7.10

^ permalink raw reply related

* [PATCH net-next 02/15] net: bus: Add documentation for AF_BUS
From: Vincent Sanders @ 2012-06-29 16:45 UTC (permalink / raw)
  To: netdev, linux-kernel, David S. Miller
  Cc: Javier Martinez Canillas, Vincent Sanders
In-Reply-To: <1340988354-26981-1-git-send-email-vincent.sanders@collabora.co.uk>

From: Javier Martinez Canillas <javier.martinez@collabora.co.uk>

Docuemnt the AF_BUS design, API and usage semantics.

Signed-off-by: Javier Martinez Canillas <javier.martinez@collabora.co.uk>
Signed-off-by: Vincent Sanders <vincent.sanders@collabora.co.uk>
---
 Documentation/networking/af_bus.txt |  558 +++++++++++++++++++++++++++++++++++
 1 file changed, 558 insertions(+)
 create mode 100644 Documentation/networking/af_bus.txt

diff --git a/Documentation/networking/af_bus.txt b/Documentation/networking/af_bus.txt
new file mode 100644
index 0000000..a0b078f
--- /dev/null
+++ b/Documentation/networking/af_bus.txt
@@ -0,0 +1,558 @@
+			The AF_BUS socket address family
+			================================
+
+Introduction
+------------
+
+AF_BUS is a message oriented inter process communication system.
+
+The principle features are:
+
+ - Reliable datagram based communication (all sockets are of type
+   SOCK_SEQPACKET)
+
+ - Multicast message delivery (one to many, unicast as a subset)
+
+ - Strict ordering (messages are delivered to every client in the same order)
+
+ - Ability to pass file descriptors
+
+ - Ability to pass credentials
+
+The basic concept is to provide a virtual bus on which multiple
+processes can communicate and policy is imposed by a "bus master".
+
+A process can create buses to which other processes can connect and
+communicate with each other by sending messages. Processes' addresses
+are automatically assigned by the bus on connect and are
+unique. Messages can be sent either to a process' unique address or to
+a bus multicast addresses.
+
+Netfilter rules or Berkeley Packet Filter can be used to restrict the
+messages that each peer is allowed to receive. This is especially
+important when sending to multicast addresses.
+
+Besides messages, process can send and receive ancillary data (i.e.,
+SCM_RIGHTS for passing file descriptors or SCM_CREDENTIALS for passing
+Unix credentials). In the case of a multicast message all recipients
+of a message may obtain a copy a file descriptor or credentials.
+
+A bus is created by processes connecting on an AF_BUS socket. The
+"bus master" binds itself instead of connecting to the NULL address.
+
+The socket address is made up of a path component and a numeric
+component. The path component is either a pathname or an abstract
+socket similar to a unix socket. The numeric component is used to
+uniquely identify each connection to the bus. Thus the path identifies
+a specific bus and the numeric component the attachment to that bus.
+
+The process that calls bind(2) on the socket is the owner of the bus
+and is called the bus master. The master is a special client of the
+bus and has some responsibility for the bus' operation. The master is
+assigned a fixed address with all the bits zero (0x0000000000000000).
+
+Each process connected to an AF_BUS socket has one or more addresses
+within that bus. These addresses are 64-bit unsigned integers,
+interpreted by splitting the address into two parts: the most
+significant 16 bits are a prefix identifying the type of address, and
+the remaining 48 bits are the actual client address within that
+prefix, as shown in this figure:
+
+Bit:  0             15 16                                            63
+     +----------------+------------------------------------------------+
+     |  Type prefix   |                Client address                  |
+     +----------------+------------------------------------------------+
+
+The prefix with all bits zero is reserved for use by the kernel, which
+automatically assigns one address from this prefix to each client on
+connection.  The address in this prefix with all bits zero is always
+assigned to the bus master. Addresses on the prefix 0x0000 are unique
+and will never repeat for the lifetime of the bus master.
+
+A client may have multiple addresses. When data is sent to other
+clients, those clients will always see the sender address that is in
+the prefix 0x0000 address space when calling recvmsg(2) or
+recvfrom(2). Similarly, the prefix 0x0000 address is returned by calls
+to getsockname(2) and getpeername(2).
+
+For each prefix, the address where the least significant 48 bits are
+all 1 (i.e., 0xffffffffffff) is also reserved, and can be used to send
+multicast messages to all the peers on a prefix.
+
+The non-reserved addresses in each of the remaining prefixes are
+managed by the bus master, which may assign additional addresses to
+any other connected socket.
+
+Having different name-spaces has two advantages:
+
+  - Clients can have addresses on different mutually-exclusive
+    scopes. This permits sending multicast packets to only clients
+    that have addresses on a given prefix.
+
+  - The addressing scheme can be more flexible. The kernel will only
+    assign unique addresses on the all-bits-zero prefix (0x0000) and
+    allows the bus master process to assign additional addresses to
+    clients on other prefixes.  By having different prefixes, the
+    kernel and bus master assignments will not collide.
+
+AF_BUS transport can support two network topologies. When a process
+first connects to the bus master, it can only communicate with the bus
+master. The process can't send and receive packets from other peers on
+the bus. So, from the client process point of view the network
+topology is point-to-point.
+
+The bus master can allow the connected peer to be part of the bus and
+start to communicate with other peers by setting a socket option with
+the setsockopt(2) system call using the accepted socket descriptor. At
+this point, the topology becomes a bus to the client process.
+
+Packets whose destination address is not assigned to any client are
+routed by default to the bus master (the client accepted socket
+descriptor).
+
+
+Semantics
+---------
+
+Bus features:
+
+ - Unicast and multicast addressing scheme.
+ - Ability to assign addresses from user-space with different prefixes.
+ - Automatic address assignment.
+ - Ordered packets delivery (FIFO, total ordering).
+ - File descriptor and credentials passing.
+ - Support for both point-to-point and bus network topologies.
+ - Bus control access managed from user-space.
+ - Netfilter hooks for packet sending, routing and receiving.
+
+A process (the "bus master") can create an AF_BUS bus with socket(2)
+and use bind(2) to assign an address to the bus. Then it can listen(2)
+on the created socket to start accepting incoming connections with
+accept(2).
+
+Processes can connect to the bus by creating a socket with socket(2)
+and using connect(2). The kernel will assign a unique address to each
+connection and messages can be sent and received by using BSD socket
+primitives.
+
+This uses the connect(2) semantic in a non-traditional way, with
+AF_BUS sockets, it's not possible to connect "my" socket to a specific
+peer socket whereas the traditional BSD sockets API usage, connect(2)
+either connects to stream sockets, or assigns a peer address to a
+datagram socket (so that send(2) can be used instead of sendto()).
+
+An AF_BUS socket address is represented as a combination of a bus
+address and a bus path name. Address are unique within a path. The
+unique bus address is further subdivided into a prefix and a client
+address. Thus the path identifies a specific bus and the numeric
+component the attachment to that bus.
+
+#define BUS_PATH_MAX    108
+
+/* Bus address */
+struct bus_addr {
+	uint64_t    s_addr; 	/* 16-bit prefix + 48-bit client address */
+};
+
+/* Structure describing an AF_BUS socket address. */
+struct sockaddr_bus {
+	sa_family_t     sbus_family; 	   	  /* AF_BUS */
+	struct bus_addr sbus_addr;                /* bus address */
+	char 		sbus_path[BUS_PATH_MAX];  /* pathname */
+};
+
+A process becomes a bus master for a given struct sockaddr_bus by
+calling bind(2) on an AF_BUS addresses. The argument must be { AF_BUS,
+0, path }. 
+
+AF_BUS supports both abstract and non-abstract path names. Abstract
+names are distinguished by the fact that sbus_path[0] == '\0' and they
+don't represent file system paths while non-abstract paths are bound
+to a file system path name. (See the unix(7) man page for a discussion
+of abstract socket addresses in the AF_UNIX address family.)
+
+Then the process calls listen(2) to accept incoming connections. If
+that process calls getsockname(2), the returned address will be {
+AF_BUS, 0, path }.
+
+The conventional string form of the full address is path + ":" +
+prefix + "/" + client address. Prefix and client address are
+represented in hex.
+
+For example the address:
+
+struct sockaddr_bus addr;
+addr.sbus_family = AF_BUS;
+strcpy(addr.sbus_path, "/tmp/test");
+addr.sbus_addr.s_addr   = 0x0002f00ddeadbeef;
+
+would be represented using the string /tmp/test:0002/f00ddeadbeef.
+
+If the bus_addr is 0, then both the prefix and client address may be
+omitted from the string form.  To connect to a bus as a client it is
+sufficient to specify the path, since the listening address always has
+bus_addr == 0. it is not meanigful to specify 'bus_addr' as other than
+0 on connect()
+
+The AF_BUS implementation will automatically assign a unique address
+to each client but the bus master can assign additional addresses on a
+different prefix by means of the setsockopt(2) system call. For
+example:
+
+struct bus_addr addr;
+addr.s_addr = 0x0001deadfee1dead;
+ret = setsockopt(afd, SOL_BUS, BUS_ADD_ADDR, &addr, sizeof(addr));
+
+where afd is the accepted socket descriptor in the daemon. To show graphically:
+
+	  L          The AF_BUS listening socket  }
+       /  |  \                                    }-- listener process
+     A1  A2  A3      The AF_BUS accepted sockets  }
+      |   |   |
+     C1  C2  C3      The AF_BUS connected sockets }-- client processes
+
+So if setsockopt(A1, SOL_BUS, BUS_ADD_ADDR, &addr, sizeof(addr)) is
+called, C1 will get the new address.
+
+The inverse operation is BUS_DEL_ADDR, which the bus master can use to
+remove a client socket AF_BUS address:
+
+ret = setsockopt(afd, SOL_BUS, BUS_DEL_ADDR, &addr, sizeof(addr));
+
+Besides assigning additional addresses, the bus master has to allow a
+client process to communicate with other peers on the bus using a
+setsockopt(2):
+
+ret = setsockopt(afd, SOL_BUS, BUS_JOIN_BUS, NULL, 0);
+
+Clients are not meant to send messages to each other until the master
+tells them (in a protocol-specific way) that the BUS_JOIN_BUS
+setsockopt(2) call was made.
+
+If a client sends a message to a destination other than the bus
+master's all-zero address before joining the bus, a EHOSTUNREACH (No
+route to host) error is returned since the only host that exists in
+the point-to-point network before the client joins the bus are the
+client and the bus master.  
+
+A EHOSTUNREACH is returned if a client that joined a bus tries to send
+a packet to a client from another bus. Cross-bus communication is not
+permited.
+
+When a process wants to send a unicast message to a peer, it fills a
+sockaddr structure and performs a socket operation (i.e., sendto(2))
+
+struct sockaddr_bus addr;
+char *msg = "Hello world";
+
+addr.sbus_family 	   = AF_BUS;
+strcpy(addr.sbus_path, "/tmp/test");
+addr.sbus_addr.s_addr   = 0x0001f00ddeadbeef;
+
+ret = sendto(sockfd, "Hello world", strlen("Hello world"), 0,
+	    (struct sockaddr*)&addr, sizeof(addr));
+
+The current implementation requires that the addr.sbus_path component
+match the one used to conenct() to the bus but in future this
+requirement will be removed.
+
+The kernel will first check that the socket is connected and that the
+bus path of the socket correspond with the destination, then it will
+extract the prefix and client address from the bus address using a
+fixed 16 -bit bitmask.
+
+prefix 		= bus address >> 48 & 0xffff
+client address 	= bus address & 0xffff
+
+If the client address is not all bits one, then the message is unicast
+and is delivered to the socket with that assigned address
+(0x0001f00ddeadbeef).  Otherwise the message is multicast and is
+delivered to all the peers with this address prefix (0x0001 in this
+case).
+
+So, when a process wants to send a multicast message, it just has to
+fill the address structure with the address prefix + 0xffffffffffff:
+
+struct sockaddr_bus addr;
+char *msg = "Hello world";
+
+addr.bus_family = AF_BUS;
+strcpy(addr.sbus_path, "/tmp/test");
+addr.bus_addr   = 0x0001ffffffffffff;
+
+ret = sendto(sockfd, "Hello world", strlen("Hello world"), 0,
+	    (struct sockaddr*)&addr, sizeof(addr));
+
+The kernel, will apply the binary and operation, learn that the
+address is 0xffffffffffff and send the message to all the peers on
+this prefix (0x0001).
+
+Socket transmit queued bytes are limited by a maximum send buffer size
+(sysctl_wmem_max) defined in the kernel and can be modified at runtime
+using the sysctl interface on /proc/sys/net/core/wmem_default. This
+parameter is global for all the sockets families in a Linux system.
+
+AF_BUS permits the definition of a per-bus maximum send buffer size
+using the BUS_SET_SENDBUF socket option. The bus master can call the
+setsockopt(2) system call using as a parameter the listening socket.
+The command sets a maximum write buffer that will be imposed on each
+new socket that connects to the bus:
+
+ret = setsockopt(serverfd, SOL_BUS, BUS_SET_SENDBUF, &sndbuf,
+sizeof(int));
+
+In the transmission path both Berkeley Packet Filters and Netfilter
+hooks are available, so they can be used to filter sending packets.
+
+
+Using this addressing scheme with D-Bus
+---------------------------------------
+
+As an example of a use case for AF_BUS, let's analyze how the D-Bus
+IPC system can be implemented on top of it.
+
+We define a new D-Bus address type "afbus".
+
+A D-Bus client may connect to an address of the form "afbus:path=X"
+where X is a string. This means that it connect()s to { AF_BUS, 0, X }.
+
+For example: afbus:path=/tmp/test connects to { AF_BUS, 0, /tmp/test }.
+
+A D-Bus daemon may listen on the address "afbus:", which means that it
+binds to { AF_BUS, 0, /tmp/test }. It will advertise an address of the
+form "afbus:path=/tmp/test" to clients, for instance via the
+--print-address option, or via dbus-launch setting the
+DBUS_SESSION_BUS_ADDRESS environment variable.  For instance, "afbus:"
+is an appropriate default listening address for the session bus,
+resulting in dbus-launch setting the DBUS_SESSION_BUS_ADDRESS
+environment variable to something like
+"afbus:path=/tmp/test,guid=...".
+
+A D-Bus daemon may listen on the address "afbus:file=/some/file",
+which means that it will do as above, then write its path into the
+given well-known file.  For instance,
+"afbus:file=/run/dbus/system_bus.afbus" is an appropriate listening
+address for the system bus. Only processes with suitable privileges to
+write to that file can impersonate the system bus.
+
+D-Bus clients wishing to connect to the well-known system bus should
+attempt to connect to afbus:file=/run/dbus/system_bus.afbus, falling
+back to unix:path=/var/run/dbus/system_bus_socket if that fails. On
+Linux systems, the well-known system bus daemon should attempt to
+listen on both of those addresses.
+
+The D-Bus daemon will serve as bus master as well since it will be the
+process that creates and listens on the AF_BUS socket.
+
+D-Bus clients will use the fixed bus master address (all zero bits) to
+send messages to the D-Bus daemon and the client's unique address to
+send messages to other D-Bus clients using the bus.
+
+When initially connected, D-Bus clients will only be able to
+communicate with the D-Bus daemon and will send authentication
+information (AUTH message and SCM_CREDENTIALS ancillary
+messages). Since the D-Bus daemon is also the bus master, it can allow
+D-Bus clients to join the bus and be able to send and receive D-Bus
+messages from other peers.
+
+On connection, the kernel will assign to each client an address in the
+prefix 0x0000. If a client attempts to send messages to clients other
+than the bus master, this is considered to be an error, and is
+prevented by the kernel.
+
+When the D-Bus daemon has authenticated a client and determined that
+it is authorized to be on this bus, it uses a setsockopt(2) call to
+tell the kernel that this client has permission to send messages. The
+D-Bus daemon then tells the client by sending the Hello() reply that
+it has made the setsockopt(2) call and that now is able to send
+messages to other peers on the bus.
+
+Well-known names are represented by addresses in the 0x0001, ... prefixes.
+
+Addresses in prefix 0x0000 must be mapped to D-Bus unique names in a
+way that can't collide with unique names allocated by the dbus-daemon
+for legacy clients.
+
+In order to be consistent with current D-Bus unique naming, the AF_BUS
+addresses can be mapped directly to D-Bus unique names, for example
+(0000/0000deadbeef to ":0.deadbeef"). Leading zeroes can be suppressed
+since the common case should be relatively small numbers (the kernel
+allocates client addresses sequentially, and machines could be
+rebooted occasionally).
+
+By having both AF_BUS and legacy D-Bus clients use the same address
+space, the D-Bus daemon can act as a proxy between clients and can be
+sure that D-Bus unique names will be unique for both AF_BUS and legacy
+clients.
+
+To act as a proxy between AF_BUS and legacy clients, each time the
+D-Bus daemon accepts a legacy connection (i.e., AF_UNIX), it will
+create an AF_BUS socket and establish a connection with itself. It
+will then associate this newly created connection with the legacy one.
+
+To explain it graphically:
+
+	  L          The AF_BUS listening socket  }
+       /  |  \                                    }-- listener process
+     A1  A2  A3      The AF_BUS accepted sockets  }
+      |   |   |
+     C1  C2  C3      The AF_BUS connected sockets, where:
+      |                    * C1 belongs to the listener process
+      |                    * C2 and C3 belongs to the client processes
+      |
+ L2--A4       The AF_UNIX listening and accepted sockets \
+      |                            in the listener process
+     C4       The AF_UNIX connected socket in the legacy client process
+
+
+where C2 and C3 are normal AF_BUS clients and C4 is a legacy
+client. The D-Bus daemon after accepting the connection using the
+legacy transport (A4), will create an AF_BUS socket pair (C1, A1)
+associated with the legacy client.
+
+Legacy clients will send messages to the D-Bus daemon using their
+legacy socket and the D-Bus daemon will extract the destination
+address, resolve to the corresponding AF_BUS address and use this to
+send the message to the right peer.  
+
+Conversely, when an AF_BUS client sends a D-Bus message to a legacy
+client, it will use the AF_BUS address of the connection associated
+with that client. The D-Bus daemon will receive the message, modify
+the message's content to set SENDER headers based on the AF_BUS source
+address and use the legacy transport to send the D-Bus message to the
+legacy client.
+
+As a special case, the bus daemon's all-zeroes address maps to
+"org.freedesktop.DBus" and vice versa.
+
+When a D-Bus client receives an AF_BUS message from the bus master
+(0/0), it must use the SENDER header field in the D-Bus message, as
+for any other D-Bus transport, to determine whether the message is
+actually from the D-Bus daemon (the SENDER is "org.freedesktop.DBus"
+or missing), or from another client (the SENDER starts with ":"). It
+is valid for messages from another AF_BUS client to be received via
+the D-Bus daemon; if they are, the SENDER header field will always be
+set.
+
+Besides its unique name, D-Bus services can have well-known names such
+as org.gnome.Keyring or org.freedesktop.Telepathy. These well-known
+names can also be used as a D-Bus message destination
+address. Well-known names are not numeric and AF_BUS is not able to
+parse D-Bus messages.
+
+To solve this, the D-Bus daemon will assign an additional AF_BUS
+address to each D-Bus client that owns a well-known name. The mapping
+between well-known names and AF_BUS address is maintained by the D-Bus
+daemon on a persistent data structure.
+
+D-Bus client libraries will maintain a cache of these mappings so they
+can send messages to services with well-known names using their mapped
+AF_BUS address.
+
+If a client intending to send a D-Bus message to a given well-known
+name does not have that well-known name in its cache, it must send the
+AF_BUS message to the listener (0000/000000000000) instead. 
+
+The listener must forward the D-Bus message to the owner of that
+well-known name, setting the SENDER header field if necessary. It may
+also send this AF_BUS-specific D-Bus signal to the sender, so that the
+sender can update its cache:
+
+     org.freedesktop.DBus.AF_BUS.Forwarded (STRING well_known_name,
+	 UINT64 af_bus_client)
+
+	 Emitted by the D-Bus daemon with sender "org.freedesktop.DBus"
+	 and object path "/org/freedesktop/DBus" to indicate that
+	 the well-known name well_known_name is represented by the
+	 AF_BUS address { AF_BUS, af_bus_client, path } where
+	 path is the path name used by this bus.
+
+	 For instance, if the well-known name "org.gnome.Keyring"
+	 is represented by AF_BUS address 0001/0000deadbeef,
+	 the signal would have arguments ("org.gnome.Keyring",
+	 0x00010000deadbeef), corresponding to the AF_BUS
+	 address { AF_BUS, 0x00010000deadbeef, /tmp/test }.
+
+If the D-Bus service for that well-known name is not active, then the
+D-Bus daemon will first do the service activation, assign an
+additional address to the recently activated service, store the
+well-known service to numeric address mapping on its persistent cache,
+and then send the AF_BUS.Forwarded signal back to the client.
+
+Once the mapping has been made, the AF_BUS address associated with a
+well-known name cannot be reused for the lifetime of the D-Bus daemon
+(which is the same as the lifetime of the socket). 
+
+Nevertheless the AF_BUS address associated with a well-known name can
+change, for example if a service goes away and a new instance gets
+activated. This new instance can have a different AF_BUS address.  The
+D-Bus daemon will maintain a list of the mappings that are currently
+valid so it can send the AF_BUS.
+
+Forwarded signal with the mapping information to the clients. Client
+libraries will maintain a fixed-size Last Recently Used (LRU) cache
+with previous mappings sent by the D-Bus daemon.
+
+If the clients overwrite a mapping due to the LRU replace policy and
+later want to send a D-Bus message to the overwritten well-known name,
+they will send the D-Bus message back to the D-Bus daemon and this
+will send the signal with the mapping information. 
+
+If a service goes away or if the service AF_BUS address changed and
+the client still has the old AF_BUS address in its cache, it will send
+the D-Bus message to the old destination. 
+
+Since packets whose destination AF_BUS addresses are not assigned to
+any process are routed by default to the bus master, the D-Bus daemon
+will receive these D-bus messages and send an AF_BUS.
+
+Forwarded signal back to the client with the new AF_BUS address so it
+can update its cache with the new mapping.
+
+For well-known names, the D-Bus daemon will use a different address
+prefix (0x0001) so it doesn't conflict with the D-Bus unique names
+address prefix (0x0000).
+
+Besides D-Bus method call messages which are unicast, D-Bus allows
+clients to send multicast messages (D-Bus signals). Clients can send
+signals messages using the bus unique name prefix multicast address
+(0x0001ffffffffffff).
+
+A netfilter hook is used to filter these multicast messages and only
+deliver to the correct peers based on match rules.
+
+
+D-Bus aware netfilter module
+----------------------------
+
+AF_BUS is designed to be a generic bus transport supporting both
+unicast and multicast communications.
+
+In order for D-Bus to operate efficiently, the transport method has to
+know the D-Bus message wire-protocol and D-Bus message structure. But
+adding this D-Bus specific knowledge to AF_BUS will break one of the
+fundamental design principles of any network protocol stack, namely
+layer-independence: layer n must not make any assumptions about the
+payload in layer n + 1.
+
+So, in order to have a clean protocol design but be able to allow the
+transport to analyze the D-Bus messages, netfilter hooks are used to
+do the filtering based on match rules.
+
+The kernel module has to maintain the match rules and the D-Bus daemon
+is responsible for managing this information. Every time an add match
+rule message is processed by the D-Bus daemon, this will update the
+netfilter module match rules set so the netfilter hook function can
+use that information to do the match rules based filtering.
+
+The D-Bus daemon and the netfilter module will use the generic netlink
+subsystem to do the kernel-to-user-space communication. Netlink is
+already used by most of the networking subsystem in Linux
+(iptables/netfilter, ip/routing, etc).
+
+We enforce a security scheme so only the bus master's user ID can
+update the netfilter module match rules set.
+
+The advantage of using the netfilter subsystem is that we decouple the
+mechanism from the policy. AF_BUS will only add a set of hook points
+and external modules will be used to enforce a given policy.
-- 
1.7.10

^ permalink raw reply related

* [PATCH net-next 03/15] net: bus: Add AF_BUS socket and address definitions
From: Vincent Sanders @ 2012-06-29 16:45 UTC (permalink / raw)
  To: netdev, linux-kernel, David S. Miller
  Cc: Javier Martinez Canillas, Vincent Sanders
In-Reply-To: <1340988354-26981-1-git-send-email-vincent.sanders@collabora.co.uk>

From: Javier Martinez Canillas <javier.martinez@collabora.co.uk>

An AF_BUS socket address is made up of a path component and a numeric
component. The path component is either a pathname or an abstract
socket similar to a unix socket. The numeric component is used to
uniquely identify each connection to the bus. Thus the path identifies
a specific bus and the numeric component the attachment to that bus.

The numeric component of the address is a 64-bit unsigned integer,
interpreted by splitting the into two parts: the most significant 16
bits are a prefix identifying the type of address, and the remaining
48 bits are the actual client address within that prefix, as shown in
this figure:

Bit:  0             15 16                                            63
     +----------------+------------------------------------------------+
     |  Type prefix   |                Client address                  |
     +----------------+------------------------------------------------+

Signed-off-by: Javier Martinez Canillas <javier.martinez@collabora.co.uk>
Signed-off-by: Vincent Sanders <vincent.sanders@collabora.co.uk>
---
 include/linux/bus.h  |   34 +++++++
 include/net/af_bus.h |  272 ++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 306 insertions(+)
 create mode 100644 include/linux/bus.h
 create mode 100644 include/net/af_bus.h

diff --git a/include/linux/bus.h b/include/linux/bus.h
new file mode 100644
index 0000000..19cac36
--- /dev/null
+++ b/include/linux/bus.h
@@ -0,0 +1,34 @@
+#ifndef _LINUX_BUS_H
+#define _LINUX_BUS_H
+
+#include <linux/socket.h>
+
+/* 'protocol' to use in socket(AF_BUS, SOCK_SEQPACKET, protocol) */
+#define BUS_PROTO_NONE	0
+#define BUS_PROTO_DBUS	1
+#define BUS_PROTO_MAX	1
+
+#define BUS_PATH_MAX	108
+
+/**
+ * struct bus_addr - af_bus address
+ * @s_addr: an af_bus address (16-bit prefix + 48-bit client address)
+ */
+struct bus_addr {
+	u64 s_addr;
+};
+
+
+/**
+ * struct sockaddr_bus - af_bus socket address
+ * @sbus_family: the socket address family
+ * @sbus_addr: an af_bus address
+ * @sbus_path: a path name
+ */
+struct sockaddr_bus {
+	__kernel_sa_family_t sbus_family;
+	struct bus_addr      sbus_addr;
+	char sbus_path[BUS_PATH_MAX];
+};
+
+#endif /* _LINUX_BUS_H */
diff --git a/include/net/af_bus.h b/include/net/af_bus.h
new file mode 100644
index 0000000..19bd7ac
--- /dev/null
+++ b/include/net/af_bus.h
@@ -0,0 +1,272 @@
+/*
+ * Copyright (c) 2012, GENIVI Alliance
+ *
+ * Authors:	Javier Martinez Canillas, <javier.martinez@collabora.co.uk>
+ *              Alban Crequy, <alban.crequy@collabora.co.uk>
+ *
+ *		This program is free software; you can redistribute it and/or
+ *		modify it under the terms of the GNU General Public License
+ *		as published by the Free Software Foundation; either version
+ *		2 of the License, or (at your option) any later version.
+ *
+ * Based on BSD Unix domain sockets (net/unix).
+ */
+
+#ifndef __LINUX_NET_AFBUS_H
+#define __LINUX_NET_AFBUS_H
+
+#include <linux/socket.h>
+#include <linux/bus.h>
+#include <linux/mutex.h>
+#include <net/sock.h>
+#include <net/tcp_states.h>
+
+extern void bus_inflight(struct file *fp);
+extern void bus_notinflight(struct file *fp);
+extern void bus_gc(void);
+extern void wait_for_bus_gc(void);
+extern struct sock *bus_get_socket(struct file *filp);
+extern struct sock *bus_peer_get(struct sock *);
+
+#define BUS_HASH_SIZE	256
+
+extern spinlock_t bus_address_lock;
+extern struct hlist_head bus_address_table[BUS_HASH_SIZE];
+
+#define BUS_MAX_QLEN    10
+#define BUS_MASTER_ADDR 0x0
+#define BUS_PREFIX_BITS 16
+#define BUS_CLIENT_BITS 48
+#define BUS_PREFIX_MASK 0xffff000000000000
+#define BUS_CLIENT_MASK 0x0000ffffffffffff
+
+/* AF_BUS socket options */
+#define BUS_ADD_ADDR 1
+#define BUS_JOIN_BUS 2
+#define BUS_DEL_ADDR 3
+#define BUS_SET_EAVESDROP 4
+#define BUS_UNSET_EAVESDROP 5
+#define BUS_SET_SENDBUF 6
+#define BUS_SET_MAXQLEN 7
+
+/* Connection and socket states */
+enum {
+	BUS_ESTABLISHED = TCP_ESTABLISHED,
+	BUS_CLOSE = TCP_CLOSE,
+	BUS_LISTEN = TCP_LISTEN,
+	BUS_MAX_STATES
+};
+
+#define NF_BUS_SENDING 1
+
+extern unsigned int bus_tot_inflight;
+extern spinlock_t bus_table_lock;
+extern struct hlist_head bus_socket_table[BUS_HASH_SIZE + 1];
+
+/**
+ * struct bus_address - an af_bus address associated with an af_bus sock
+ * @refcnt: address reference counter
+ * @len: address length
+ * @hash: address hash value
+ * @addr_node: member of struct bus_sock.addr_list
+ * @table_node: member of struct hlist_head bus_address_table[hash]
+ * @sock: the af_bus sock that owns this address
+ * @name: the socket address for this address
+ */
+struct bus_address {
+	atomic_t	refcnt;
+	int		len;
+	unsigned	hash;
+	struct hlist_node addr_node;
+	struct hlist_node table_node;
+	struct sock  *sock;
+	struct sockaddr_bus name[0];
+};
+
+/**
+ * struct bus_send_context - sending context for an socket buffer
+ * @sender_socket: the sender socket associated with this sk_buff
+ * @siocb: used to send ancillary data
+ * @timeo: sending timeout
+ * @max_level: file descriptor passing maximum recursion level
+ * @namelen: length of socket address name
+ * @hash: socket name hash value
+ * @other: destination sock
+ * @sender: sender socket address name
+ * @recipient: recipient socket address name
+ * @authenticated: flag whether the sock already joined the bus
+ * @bus_master_side: flag whether the sock is an accepted socket
+ * @to_master: flag whether the destination is the bus master
+ * @multicast: flag whether the destination is a multicast address
+ * @deliver: flag whether the skb has to be delivered
+ * @eavesdropper: flag whether the sock is allowed to eavesdrop
+ * @main_recipient: flag whether the sock is the main recipient
+ */
+struct bus_send_context {
+	struct socket *sender_socket;
+	struct sock_iocb *siocb;
+	long timeo;
+	int max_level;
+	int namelen;
+	unsigned hash;
+	struct sock *other;
+	struct sockaddr_bus	*sender;
+	struct sockaddr_bus	*recipient;
+	unsigned int		authenticated:1;
+	unsigned int		bus_master_side:1;
+	unsigned int		to_master:1;
+	unsigned int		multicast:1;
+	unsigned int            deliver:1;
+	unsigned int            eavesdropper:1;
+	unsigned int            main_recipient:1;
+};
+
+/**
+ * struct bus_skb_parms - socket buffer parameters
+ * @pid: process id
+ * @cred: skb credentials
+ * @fp: passed file descriptors
+ * @secid: security id
+ * @sendctx: skb sending context
+ */
+struct bus_skb_parms {
+	struct pid		*pid;
+	const struct cred	*cred;
+	struct scm_fp_list	*fp;
+#ifdef CONFIG_SECURITY_NETWORK
+	u32			secid;
+#endif
+	struct bus_send_context	*sendctx;
+};
+
+#define BUSCB(skb)      (*(struct bus_skb_parms *)&((skb)->cb))
+#define BUSSID(skb)     (&BUSCB((skb)).secid)
+
+#define bus_state_lock(s)	spin_lock(&bus_sk(s)->lock)
+#define bus_state_unlock(s)	spin_unlock(&bus_sk(s)->lock)
+#define bus_state_lock_nested(s) \
+				spin_lock_nested(&bus_sk(s)->lock, \
+				SINGLE_DEPTH_NESTING)
+
+/**
+ * struct bus - a communication bus
+ * @master: the bus master sock
+ * @peers: list of struct bus_sock.bus_node allowed to join the bus
+ * @lock: protect peers concurrent access
+ * @send_lock: enforce atomic multicast delivery
+ * @kref: bus reference counter
+ * @addr_cnt: address number counter to assign prefix 0x0000 addresses
+ * @eavesdropper_cnt: eavesdroppers counter
+ */
+struct bus {
+	struct sock		*master;
+	struct hlist_head       peers;
+	spinlock_t		lock;
+	spinlock_t		send_lock;
+	struct kref             kref;
+	atomic64_t              addr_cnt;
+	atomic64_t              eavesdropper_cnt;
+};
+
+/**
+ * struct bus_sock - an af_bus socket
+ * @sk: associated sock
+ * @addr: sock principal address
+ * @addr_list: list of struct bus_address.addr_node
+ * @path: sock path name
+ * @readlock: protect from concurrent reading
+ * @peer: peer sock
+ * @other: the listening sock
+ * @link: list of candidates for garbage collection
+ * @inflight: number of times the file descriptor is in flight
+ * @lock: protect the sock from concurrent access
+ * @gc_candidate: flag whether the is a candidate for gc
+ * @gc_maybe_cycle: flag whether could be a cyclic reference
+ * @recursion_level: file passing current recursion level
+ * @peer_wq: peer sock wait queue
+ * @bus: bus that this sock belongs to
+ * @bus_master: flag whether the sock is the bus master
+ * @bus_master_side: flag whether is an accepted socket
+ * @authenticated: flag whether the sock joined the bus
+ * @eavesdropper: flag whether the sock is allowed to eavesdrop
+ * @bus_node: member of struct bus.peers list of joined socks
+ */
+struct bus_sock {
+	/* WARNING: sk has to be the first member */
+	struct sock		sk;
+	struct bus_address     *addr;
+	struct hlist_head       addr_list;
+	struct path		path;
+	struct mutex		readlock;
+	struct sock		*peer;
+	struct sock		*other;
+	struct list_head	link;
+	atomic_long_t		inflight;
+	spinlock_t		lock;
+	unsigned int		gc_candidate:1;
+	unsigned int		gc_maybe_cycle:1;
+	unsigned char		recursion_level;
+	struct socket_wq	peer_wq;
+	struct bus              *bus;
+	bool                    bus_master;
+	bool                    bus_master_side;
+	bool                    authenticated;
+	bool                    eavesdropper;
+	struct hlist_node	bus_node;
+};
+#define bus_sk(__sk) ((struct bus_sock *)__sk)
+
+#define peer_wait peer_wq.wait
+
+/**
+ * bus_same_bus - Test if two socket address belongs to the same bus
+ * @sbusaddr1: socket address name
+ * @sbusaddr2: socket address name
+ */
+static inline bool bus_same_bus(struct sockaddr_bus *sbusaddr1,
+				struct sockaddr_bus *sbusaddr2)
+{
+	int offset;
+
+	if (sbusaddr1->sbus_path[0] != sbusaddr2->sbus_path[0])
+		return false;
+
+	/*
+	 * abstract path names start with a null byte character,
+	 * so they have to be compared starting at the second char.
+	 */
+	offset = (sbusaddr1->sbus_path[0] == '\0');
+
+	return !strncmp(sbusaddr1->sbus_path + offset,
+		       sbusaddr2->sbus_path + offset,
+		       BUS_PATH_MAX);
+}
+
+static inline unsigned int bus_hash_fold(__wsum n)
+{
+	unsigned int hash = (__force unsigned int)n;
+	hash ^= hash>>16;
+	hash ^= hash>>8;
+	return hash&(BUS_HASH_SIZE-1);
+}
+
+static inline unsigned int bus_compute_hash(struct bus_addr addr)
+{
+	return bus_hash_fold(csum_partial((void *)&addr, sizeof(addr), 0));
+}
+
+long bus_inq_len(struct sock *sk);
+long bus_outq_len(struct sock *sk);
+
+#ifdef CONFIG_SYSCTL
+extern int bus_sysctl_register(struct net *net);
+extern void bus_sysctl_unregister(struct net *net);
+#else
+static inline int bus_sysctl_register(struct net *net) { return 0; }
+static inline void bus_sysctl_unregister(struct net *net) {}
+#endif
+
+bool bus_can_write(struct net *net, struct sockaddr_bus *addr, int len,
+		   int protocol);
+
+#endif /* __LINUX_NET_AFBUS_H */
-- 
1.7.10

^ permalink raw reply related

* [PATCH net-next 04/15] security: Add Linux Security Modules hook for AF_BUS sockets
From: Vincent Sanders @ 2012-06-29 16:45 UTC (permalink / raw)
  To: netdev, linux-kernel, David S. Miller
  Cc: Javier Martinez Canillas, Vincent Sanders
In-Reply-To: <1340988354-26981-1-git-send-email-vincent.sanders@collabora.co.uk>

From: Javier Martinez Canillas <javier.martinez@collabora.co.uk>

AF_BUS implements a security hook bus_connect() to be used by LSM to
enforce connectivity security policies.

Signed-off-by: Javier Martinez Canillas <javier.martinez@collabora.co.uk>
Signed-off-by: Vincent Sanders <vincent.sanders@collabora.co.uk>
---
 include/linux/security.h |   11 +++++++++++
 security/capability.c    |    7 +++++++
 security/security.c      |    7 +++++++
 3 files changed, 25 insertions(+)

diff --git a/include/linux/security.h b/include/linux/security.h
index 4e5a73c..d30dc4a 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -1578,6 +1578,8 @@ struct security_operations {
 
 #ifdef CONFIG_SECURITY_NETWORK
 	int (*unix_stream_connect) (struct sock *sock, struct sock *other, struct sock *newsk);
+	int (*bus_connect) (struct sock *sock, struct sock *other,
+			    struct sock *newsk);
 	int (*unix_may_send) (struct socket *sock, struct socket *other);
 
 	int (*socket_create) (int family, int type, int protocol, int kern);
@@ -2519,6 +2521,8 @@ static inline int security_inode_getsecctx(struct inode *inode, void **ctx, u32
 #ifdef CONFIG_SECURITY_NETWORK
 
 int security_unix_stream_connect(struct sock *sock, struct sock *other, struct sock *newsk);
+int security_bus_connect(struct sock *sock, struct sock *other,
+			 struct sock *newsk);
 int security_unix_may_send(struct socket *sock,  struct socket *other);
 int security_socket_create(int family, int type, int protocol, int kern);
 int security_socket_post_create(struct socket *sock, int family,
@@ -2566,6 +2570,13 @@ static inline int security_unix_stream_connect(struct sock *sock,
 	return 0;
 }
 
+static inline int security_bus_connect(struct socket *sock,
+				       struct sock *other,
+				       struct sock *newsk)
+{
+	return 0;
+}
+
 static inline int security_unix_may_send(struct socket *sock,
 					 struct socket *other)
 {
diff --git a/security/capability.c b/security/capability.c
index 61095df..ea57f2b 100644
--- a/security/capability.c
+++ b/security/capability.c
@@ -563,6 +563,12 @@ static int cap_unix_may_send(struct socket *sock, struct socket *other)
 	return 0;
 }
 
+static int cap_bus_connect(struct sock *sock, struct sock *other,
+			   struct sock *newsk)
+{
+	return 0;
+}
+
 static int cap_socket_create(int family, int type, int protocol, int kern)
 {
 	return 0;
@@ -1016,6 +1022,7 @@ void __init security_fixup_ops(struct security_operations *ops)
 #ifdef CONFIG_SECURITY_NETWORK
 	set_to_cap_if_null(ops, unix_stream_connect);
 	set_to_cap_if_null(ops, unix_may_send);
+	set_to_cap_if_null(ops, bus_connect);
 	set_to_cap_if_null(ops, socket_create);
 	set_to_cap_if_null(ops, socket_post_create);
 	set_to_cap_if_null(ops, socket_bind);
diff --git a/security/security.c b/security/security.c
index 3efc9b1..00ab7df 100644
--- a/security/security.c
+++ b/security/security.c
@@ -1059,6 +1059,13 @@ int security_unix_may_send(struct socket *sock,  struct socket *other)
 }
 EXPORT_SYMBOL(security_unix_may_send);
 
+int security_bus_connect(struct sock *sock, struct sock *other,
+				struct sock *newsk)
+{
+	return security_ops->bus_connect(sock, other, newsk);
+}
+EXPORT_SYMBOL(security_bus_connect);
+
 int security_socket_create(int family, int type, int protocol, int kern)
 {
 	return security_ops->socket_create(family, type, protocol, kern);
-- 
1.7.10

^ permalink raw reply related

* [PATCH net-next 05/15] security: selinux: Add AF_BUS socket SELinux hooks
From: Vincent Sanders @ 2012-06-29 16:45 UTC (permalink / raw)
  To: netdev, linux-kernel, David S. Miller
  Cc: Javier Martinez Canillas, Vincent Sanders
In-Reply-To: <1340988354-26981-1-git-send-email-vincent.sanders@collabora.co.uk>

From: Javier Martinez Canillas <javier.martinez@collabora.co.uk>

Add Security-Enhanced Linux (SELinux) hook for AF_BUS socket address family.

Signed-off-by: Javier Martinez Canillas <javier.martinez@collabora.co.uk>
Signed-off-by: Vincent Sanders <vincent.sanders@collabora.co.uk>
---
 security/selinux/hooks.c |   35 +++++++++++++++++++++++++++++++++++
 1 file changed, 35 insertions(+)

diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 4ee6f23..5bacbe2 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -67,6 +67,7 @@
 #include <linux/quota.h>
 #include <linux/un.h>		/* for Unix socket types */
 #include <net/af_unix.h>	/* for Unix socket types */
+#include <net/af_bus.h>	/* for Bus socket types */
 #include <linux/parser.h>
 #include <linux/nfs_mount.h>
 #include <net/ipv6.h>
@@ -4101,6 +4102,39 @@ static int selinux_socket_unix_may_send(struct socket *sock,
 			    &ad);
 }
 
+static int selinux_socket_bus_connect(struct sock *sock, struct sock *other,
+				      struct sock *newsk)
+{
+	struct sk_security_struct *sksec_sock = sock->sk_security;
+	struct sk_security_struct *sksec_other = other->sk_security;
+	struct sk_security_struct *sksec_new = newsk->sk_security;
+	struct common_audit_data ad;
+	struct lsm_network_audit net = {0,};
+	int err;
+
+	ad.type = LSM_AUDIT_DATA_NET;
+	ad.u.net = &net;
+	ad.u.net->sk = other;
+
+	err = avc_has_perm(sksec_sock->sid, sksec_other->sid,
+			   sksec_other->sclass,
+			   UNIX_STREAM_SOCKET__CONNECTTO, &ad);
+	if (err)
+		return err;
+
+	/* server child socket */
+	sksec_new->peer_sid = sksec_sock->sid;
+	err = security_sid_mls_copy(sksec_other->sid, sksec_sock->sid,
+				    &sksec_new->sid);
+	if (err)
+		return err;
+
+	/* connecting socket */
+	sksec_sock->peer_sid = sksec_new->sid;
+
+	return 0;
+}
+
 static int selinux_inet_sys_rcv_skb(int ifindex, char *addrp, u16 family,
 				    u32 peer_sid,
 				    struct common_audit_data *ad)
@@ -5643,6 +5677,7 @@ static struct security_operations selinux_ops = {
 
 	.unix_stream_connect =		selinux_socket_unix_stream_connect,
 	.unix_may_send =		selinux_socket_unix_may_send,
+	.bus_connect =		        selinux_socket_bus_connect,
 
 	.socket_create =		selinux_socket_create,
 	.socket_post_create =		selinux_socket_post_create,
-- 
1.7.10

^ permalink raw reply related

* [PATCH net-next 06/15] netfilter: Add NFPROTO_BUS hook constant for AF_BUS socket family
From: Vincent Sanders @ 2012-06-29 16:45 UTC (permalink / raw)
  To: netdev, linux-kernel, David S. Miller
  Cc: Javier Martinez Canillas, Vincent Sanders
In-Reply-To: <1340988354-26981-1-git-send-email-vincent.sanders@collabora.co.uk>

From: Javier Martinez Canillas <javier.martinez@collabora.co.uk>

AF_BUS sockets add a netfilter NF_HOOK() on the packet sending path.
This allows packet to be mangled by registered netfilter hooks.

Signed-off-by: Javier Martinez Canillas <javier.martinez@collabora.co.uk>
Signed-off-by: Vincent Sanders <vincent.sanders@collabora.co.uk>
---
 include/linux/netfilter.h |    1 +
 1 file changed, 1 insertion(+)

diff --git a/include/linux/netfilter.h b/include/linux/netfilter.h
index c613cf0..0698924 100644
--- a/include/linux/netfilter.h
+++ b/include/linux/netfilter.h
@@ -67,6 +67,7 @@ enum {
 	NFPROTO_BRIDGE =  7,
 	NFPROTO_IPV6   = 10,
 	NFPROTO_DECNET = 12,
+	NFPROTO_BUS,
 	NFPROTO_NUMPROTO,
 };
 
-- 
1.7.10

^ permalink raw reply related

* [PATCH net-next 07/15] scm: allow AF_BUS sockets to send ancillary data
From: Vincent Sanders @ 2012-06-29 16:45 UTC (permalink / raw)
  To: netdev, linux-kernel, David S. Miller
  Cc: Javier Martinez Canillas, Vincent Sanders
In-Reply-To: <1340988354-26981-1-git-send-email-vincent.sanders@collabora.co.uk>

From: Javier Martinez Canillas <javier.martinez@collabora.co.uk>

Similar to UNIX domain sockets AF_BUS sockets support passing file
descriptors and process credentials which requires supporting passing
control messages.

The core socket level control messages processing requires extending
to allow sockets other than PF_UNIX to send SCM_RIGHTS type messages.

Signed-off-by: Javier Martinez Canillas <javier.martinez@collabora.co.uk>
Signed-off-by: Vincent Sanders <vincent.sanders@collabora.co.uk>
---
 net/core/scm.c |    3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/net/core/scm.c b/net/core/scm.c
index 611c5ef..87e3152 100644
--- a/net/core/scm.c
+++ b/net/core/scm.c
@@ -158,7 +158,8 @@ int __scm_send(struct socket *sock, struct msghdr *msg, struct scm_cookie *p)
 		switch (cmsg->cmsg_type)
 		{
 		case SCM_RIGHTS:
-			if (!sock->ops || sock->ops->family != PF_UNIX)
+			if (!sock->ops || (sock->ops->family != PF_UNIX &&
+					   sock->ops->family != PF_BUS))
 				goto error;
 			err=scm_fp_copy(cmsg, &p->fp);
 			if (err<0)
-- 
1.7.10

^ permalink raw reply related

* [PATCH net-next 08/15] net: bus: Add implementation of Bus domain sockets
From: Vincent Sanders @ 2012-06-29 16:45 UTC (permalink / raw)
  To: netdev, linux-kernel, David S. Miller
  Cc: Javier Martinez Canillas, Vincent Sanders
In-Reply-To: <1340988354-26981-1-git-send-email-vincent.sanders@collabora.co.uk>

From: Javier Martinez Canillas <javier.martinez@collabora.co.uk>

This is the core impolementation of the AF_BUS socket family its
design and operation are fully covered in
Documentation/networking/af_bus.txt

Signed-off-by: Javier Martinez Canillas <javier.martinez@collabora.co.uk>
Signed-off-by: Vincent Sanders <vincent.sanders@collabora.co.uk>
---
 net/bus/af_bus.c | 2629 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 2629 insertions(+)
 create mode 100644 net/bus/af_bus.c

diff --git a/net/bus/af_bus.c b/net/bus/af_bus.c
new file mode 100644
index 0000000..0b79754
--- /dev/null
+++ b/net/bus/af_bus.c
@@ -0,0 +1,2629 @@
+/*
+ * Implementation of Bus domain sockets.
+ *
+ * Copyright (c) 2012, GENIVI Alliance
+ *
+ * Authors:	Javier Martinez Canillas <javier.martinez@collabora.co.uk>
+ *              Alban Crequy <alban.crequy@collabora.co.uk>
+ *
+ *		This program is free software; you can redistribute it and/or
+ *		modify it under the terms of the GNU General Public License
+ *		as published by the Free Software Foundation; either version
+ *		2 of the License, or (at your option) any later version.
+ *
+ * Based on BSD Unix domain sockets (net/unix).
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/stat.h>
+#include <linux/dcache.h>
+#include <linux/namei.h>
+#include <linux/socket.h>
+#include <linux/bus.h>
+#include <linux/fcntl.h>
+#include <linux/termios.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <linux/in.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <net/net_namespace.h>
+#include <net/sock.h>
+#include <net/af_bus.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <net/scm.h>
+#include <linux/init.h>
+#include <linux/poll.h>
+#include <linux/rtnetlink.h>
+#include <linux/mount.h>
+#include <net/checksum.h>
+#include <linux/security.h>
+
+struct hlist_head bus_socket_table[BUS_HASH_SIZE + 1];
+EXPORT_SYMBOL_GPL(bus_socket_table);
+struct hlist_head bus_address_table[BUS_HASH_SIZE];
+EXPORT_SYMBOL_GPL(bus_address_table);
+DEFINE_SPINLOCK(bus_table_lock);
+DEFINE_SPINLOCK(bus_address_lock);
+EXPORT_SYMBOL_GPL(bus_address_lock);
+static atomic_long_t bus_nr_socks;
+
+#define bus_sockets_unbound	(&bus_socket_table[BUS_HASH_SIZE])
+
+#define BUS_ABSTRACT(sk)	(bus_sk(sk)->addr->hash != BUS_HASH_SIZE)
+
+#ifdef CONFIG_SECURITY_NETWORK
+static void bus_get_secdata(struct scm_cookie *scm, struct sk_buff *skb)
+{
+	memcpy(BUSSID(skb), &scm->secid, sizeof(u32));
+}
+
+static inline void bus_set_secdata(struct scm_cookie *scm, struct sk_buff *skb)
+{
+	scm->secid = *BUSSID(skb);
+}
+#else
+static inline void bus_get_secdata(struct scm_cookie *scm, struct sk_buff *skb)
+{ }
+
+static inline void bus_set_secdata(struct scm_cookie *scm, struct sk_buff *skb)
+{ }
+#endif /* CONFIG_SECURITY_NETWORK */
+
+/*
+ *  SMP locking strategy:
+ *    bus_socket_table hash table is protected with spinlock bus_table_lock
+ *    bus_address_table hash table is protected with spinlock bus_address_lock
+ *    each bus is protected by a separate spin lock.
+ *    multicast atomic sending is protected by a separate spin lock.
+ *    each socket state is protected by a separate spin lock.
+ *    each socket address is protected by a separate spin lock.
+ *
+ *  When holding more than one lock, use the following hierarchy:
+ *  - bus_table_lock.
+ *  - bus_address_lock.
+ *  - socket lock.
+ *  - bus lock.
+ *  - bus send_lock.
+ *  - sock address lock.
+ */
+
+#define bus_peer(sk) (bus_sk(sk)->peer)
+
+static inline int bus_our_peer(struct sock *sk, struct sock *osk)
+{
+	return bus_peer(osk) == sk;
+}
+
+static inline int bus_recvq_full(struct sock const *sk)
+{
+	return skb_queue_len(&sk->sk_receive_queue) > sk->sk_max_ack_backlog;
+}
+
+static inline u16 bus_addr_prefix(struct sockaddr_bus *busaddr)
+{
+	return (busaddr->sbus_addr.s_addr & BUS_PREFIX_MASK) >> BUS_CLIENT_BITS;
+}
+
+static inline u64 bus_addr_client(struct sockaddr_bus *sbusaddr)
+{
+	return sbusaddr->sbus_addr.s_addr & BUS_CLIENT_MASK;
+}
+
+static inline bool bus_mc_addr(struct sockaddr_bus *sbusaddr)
+{
+	return bus_addr_client(sbusaddr) == BUS_CLIENT_MASK;
+}
+
+struct sock *bus_peer_get(struct sock *s)
+{
+	struct sock *peer;
+
+	bus_state_lock(s);
+	peer = bus_peer(s);
+	if (peer)
+		sock_hold(peer);
+	bus_state_unlock(s);
+	return peer;
+}
+EXPORT_SYMBOL_GPL(bus_peer_get);
+
+static inline void bus_release_addr(struct bus_address *addr)
+{
+	if (atomic_dec_and_test(&addr->refcnt))
+		kfree(addr);
+}
+
+/*
+ *	Check bus socket name:
+ *		- should be not zero length.
+ *	        - if started by not zero, should be NULL terminated (FS object)
+ *		- if started by zero, it is abstract name.
+ */
+
+static int bus_mkname(struct sockaddr_bus *sbusaddr, int len,
+		      unsigned int *hashp)
+{
+	int offset = (sbusaddr->sbus_path[0] == '\0');
+
+	if (len <= sizeof(short) || len > sizeof(*sbusaddr))
+		return -EINVAL;
+	if (!sbusaddr || sbusaddr->sbus_family != AF_BUS)
+		return -EINVAL;
+
+	len = strnlen(sbusaddr->sbus_path + offset, BUS_PATH_MAX) + 1 +
+		sizeof(__kernel_sa_family_t) +
+		sizeof(struct bus_addr);
+
+	*hashp = bus_compute_hash(sbusaddr->sbus_addr);
+	return len;
+}
+
+static void __bus_remove_address(struct bus_address *addr)
+{
+	hlist_del(&addr->table_node);
+}
+
+static void __bus_insert_address(struct hlist_head *list,
+				 struct bus_address *addr)
+{
+	hlist_add_head(&addr->table_node, list);
+}
+
+static inline void bus_remove_address(struct bus_address *addr)
+{
+	spin_lock(&bus_address_lock);
+	__bus_remove_address(addr);
+	spin_unlock(&bus_address_lock);
+}
+
+static inline void bus_insert_address(struct hlist_head *list,
+				      struct bus_address *addr)
+{
+	spin_lock(&bus_address_lock);
+	__bus_insert_address(list, addr);
+	spin_unlock(&bus_address_lock);
+}
+
+static void __bus_remove_socket(struct sock *sk)
+{
+	sk_del_node_init(sk);
+}
+
+static void __bus_insert_socket(struct hlist_head *list, struct sock *sk)
+{
+	WARN_ON(!sk_unhashed(sk));
+	sk_add_node(sk, list);
+}
+
+static inline void bus_remove_socket(struct sock *sk)
+{
+	spin_lock(&bus_table_lock);
+	__bus_remove_socket(sk);
+	spin_unlock(&bus_table_lock);
+}
+
+static inline void bus_insert_socket(struct hlist_head *list, struct sock *sk)
+{
+	spin_lock(&bus_table_lock);
+	__bus_insert_socket(list, sk);
+	spin_unlock(&bus_table_lock);
+}
+
+static inline bool __bus_has_prefix(struct sock *sk, u16 prefix)
+{
+	struct bus_sock *u = bus_sk(sk);
+	struct bus_address *addr;
+	struct hlist_node *node;
+	bool ret = false;
+
+	hlist_for_each_entry(addr, node, &u->addr_list, addr_node) {
+		if (bus_addr_prefix(addr->name) == prefix)
+			ret = true;
+	}
+
+	return ret;
+}
+
+static inline bool bus_has_prefix(struct sock *sk, u16 prefix)
+{
+	bool ret;
+
+	bus_state_lock(sk);
+	ret = __bus_has_prefix(sk, prefix);
+	bus_state_unlock(sk);
+
+	return ret;
+}
+
+static inline bool __bus_eavesdropper(struct sock *sk, u16 condition)
+{
+	struct bus_sock *u = bus_sk(sk);
+
+	return u->eavesdropper;
+}
+
+static inline bool bus_eavesdropper(struct sock *sk, u16 condition)
+{
+	bool ret;
+
+	bus_state_lock(sk);
+	ret = __bus_eavesdropper(sk, condition);
+	bus_state_unlock(sk);
+
+	return ret;
+}
+
+static inline bool bus_has_prefix_eavesdropper(struct sock *sk, u16 prefix)
+{
+	bool ret;
+
+	bus_state_lock(sk);
+	ret = __bus_has_prefix(sk, prefix) || __bus_eavesdropper(sk, 0);
+	bus_state_unlock(sk);
+
+	return ret;
+}
+
+static inline struct bus_address *__bus_get_address(struct sock *sk,
+						    struct bus_addr *sbus_addr)
+{
+	struct bus_sock *u = bus_sk(sk);
+	struct bus_address *addr = NULL;
+	struct hlist_node *node;
+
+	hlist_for_each_entry(addr, node, &u->addr_list, addr_node) {
+		if (addr->name->sbus_addr.s_addr == sbus_addr->s_addr)
+			return addr;
+	}
+
+	return NULL;
+}
+
+static inline struct bus_address *bus_get_address(struct sock *sk,
+						  struct bus_addr *sbus_addr)
+{
+	struct bus_address *addr;
+
+	bus_state_lock(sk);
+	addr = __bus_get_address(sk, sbus_addr);
+	bus_state_unlock(sk);
+
+	return addr;
+}
+
+static struct sock *__bus_find_socket_byname(struct net *net,
+					     struct sockaddr_bus *sbusname,
+					     int len, unsigned int hash)
+{
+	struct sock *s;
+	struct hlist_node *node;
+
+	sk_for_each(s, node, &bus_socket_table[hash]) {
+		struct bus_sock *u = bus_sk(s);
+
+		if (!net_eq(sock_net(s), net))
+			continue;
+
+		if (u->addr->len == len &&
+		    !memcmp(u->addr->name, sbusname, len))
+			return s;
+	}
+
+	return NULL;
+}
+
+static inline struct sock *bus_find_socket_byname(struct net *net,
+						  struct sockaddr_bus *sbusname,
+						  int len, unsigned int hash)
+{
+	struct sock *s;
+
+	spin_lock(&bus_table_lock);
+	s = __bus_find_socket_byname(net, sbusname, len, hash);
+	if (s)
+		sock_hold(s);
+	spin_unlock(&bus_table_lock);
+	return s;
+}
+
+static struct sock *__bus_find_socket_byaddress(struct net *net,
+						struct sockaddr_bus *sbusname,
+						int len, int protocol,
+						unsigned int hash)
+{
+	struct sock *s;
+	struct bus_address *addr;
+	struct hlist_node *node;
+	struct bus_sock *u;
+	int offset = (sbusname->sbus_path[0] == '\0');
+	int path_len = strnlen(sbusname->sbus_path + offset, BUS_PATH_MAX);
+
+	len = path_len + 1 + sizeof(__kernel_sa_family_t) +
+	      sizeof(struct bus_addr);
+
+	hlist_for_each_entry(addr, node, &bus_address_table[hash],
+			     table_node) {
+		s = addr->sock;
+		u = bus_sk(s);
+
+		if (s->sk_protocol != protocol)
+			continue;
+
+		if (!net_eq(sock_net(s), net))
+			continue;
+
+		if (addr->len == len &&
+		    addr->name->sbus_family == sbusname->sbus_family &&
+		    addr->name->sbus_addr.s_addr == sbusname->sbus_addr.s_addr
+		    && bus_same_bus(addr->name, sbusname))
+			goto found;
+	}
+	s = NULL;
+found:
+	return s;
+}
+
+static inline struct sock *bus_find_socket_byaddress(struct net *net,
+						     struct sockaddr_bus *name,
+						     int len, int protocol,
+						     unsigned int hash)
+{
+	struct sock *s;
+
+	spin_lock(&bus_address_lock);
+	s = __bus_find_socket_byaddress(net, name, len, protocol, hash);
+	if (s)
+		sock_hold(s);
+	spin_unlock(&bus_address_lock);
+	return s;
+}
+
+static inline int bus_writable(struct sock *sk)
+{
+	return (atomic_read(&sk->sk_wmem_alloc) << 2) <= sk->sk_sndbuf;
+}
+
+static void bus_write_space(struct sock *sk)
+{
+	struct bus_sock *u = bus_sk(sk);
+	struct bus_sock *p;
+	struct hlist_node *node;
+	struct socket_wq *wq;
+
+	if (bus_writable(sk)) {
+		rcu_read_lock();
+		wq = rcu_dereference(sk->sk_wq);
+		if (wq_has_sleeper(wq))
+			wake_up_interruptible_sync_poll(&wq->wait,
+				POLLOUT | POLLWRNORM | POLLWRBAND);
+		sk_wake_async(sk, SOCK_WAKE_SPACE, POLL_OUT);
+		rcu_read_unlock();
+
+		if (u && u->bus) {
+			spin_lock(&u->bus->lock);
+			hlist_for_each_entry(p, node, &u->bus->peers,
+					     bus_node) {
+				wake_up_interruptible_sync_poll(sk_sleep(&p->sk),
+								POLLOUT |
+								POLLWRNORM |
+								POLLWRBAND);
+				sk_wake_async(&p->sk, SOCK_WAKE_SPACE,
+					      POLL_OUT);
+			}
+			spin_unlock(&u->bus->lock);
+		}
+	}
+}
+
+static void bus_bus_release(struct kref *kref)
+{
+	struct bus *bus;
+
+	bus = container_of(kref, struct bus, kref);
+
+	kfree(bus);
+}
+
+static void bus_sock_destructor(struct sock *sk)
+{
+	struct bus_sock *u = bus_sk(sk);
+
+	skb_queue_purge(&sk->sk_receive_queue);
+
+	WARN_ON(atomic_read(&sk->sk_wmem_alloc));
+	WARN_ON(!sk_unhashed(sk));
+	WARN_ON(sk->sk_socket);
+	if (!sock_flag(sk, SOCK_DEAD)) {
+		pr_info("Attempt to release alive bus socket: %p\n", sk);
+		return;
+	}
+
+	if (u->bus) {
+		kref_put(&u->bus->kref, bus_bus_release);
+		u->bus = NULL;
+	}
+
+	atomic_long_dec(&bus_nr_socks);
+	local_bh_disable();
+	sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1);
+	local_bh_enable();
+#ifdef BUS_REFCNT_DEBUG
+	pr_debug("BUS %p is destroyed, %ld are still alive.\n", sk,
+		 atomic_long_read(&bus_nr_socks));
+#endif
+}
+
+static int bus_release_sock(struct sock *sk, int embrion)
+{
+	struct bus_sock *u = bus_sk(sk);
+	struct path path;
+	struct sock *skpair;
+	struct sk_buff *skb;
+	int state;
+	struct bus_address *addr;
+	struct hlist_node *node, *tmp;
+
+	bus_remove_socket(sk);
+
+	if (u->bus && u->authenticated &&
+	    !u->bus_master && !u->bus_master_side) {
+		spin_lock(&u->bus->lock);
+		hlist_del(&u->bus_node);
+		if (u->eavesdropper)
+			atomic64_dec(&u->bus->eavesdropper_cnt);
+		spin_unlock(&u->bus->lock);
+	}
+
+	/* Clear state */
+	bus_state_lock(sk);
+	sock_orphan(sk);
+	sk->sk_shutdown = SHUTDOWN_MASK;
+	path	     = u->path;
+	u->path.dentry = NULL;
+	u->path.mnt = NULL;
+	state = sk->sk_state;
+	sk->sk_state = BUS_CLOSE;
+
+	if (u->bus_master)
+			u->bus->master = NULL;
+
+	if (u->bus_master_side) {
+		bus_release_addr(u->addr);
+		u->addr = NULL;
+	} else {
+		u->addr = NULL;
+
+		spin_lock(&bus_address_lock);
+		hlist_for_each_entry_safe(addr, node, tmp, &u->addr_list,
+					  addr_node) {
+			hlist_del(&addr->addr_node);
+			__bus_remove_address(addr);
+			bus_release_addr(addr);
+		}
+		spin_unlock(&bus_address_lock);
+	}
+
+	bus_state_unlock(sk);
+
+	wake_up_interruptible_all(&u->peer_wait);
+
+	skpair = bus_peer(sk);
+
+	if (skpair != NULL) {
+		bus_state_lock(skpair);
+		/* No more writes */
+		skpair->sk_shutdown = SHUTDOWN_MASK;
+		if (!skb_queue_empty(&sk->sk_receive_queue) || embrion)
+			skpair->sk_err = ECONNRESET;
+		bus_state_unlock(skpair);
+		skpair->sk_state_change(skpair);
+		sk_wake_async(skpair, SOCK_WAKE_WAITD, POLL_HUP);
+		sock_put(skpair); /* It may now die */
+		bus_peer(sk) = NULL;
+	}
+
+	/* Try to flush out this socket. Throw out buffers at least */
+
+	while ((skb = skb_dequeue(&sk->sk_receive_queue)) != NULL) {
+		if (state == BUS_LISTEN)
+			bus_release_sock(skb->sk, 1);
+		/* passed fds are erased in the kfree_skb hook	      */
+		kfree_skb(skb);
+	}
+
+	if (path.dentry)
+		path_put(&path);
+
+	sock_put(sk);
+
+	/* ---- Socket is dead now and most probably destroyed ---- */
+
+	if (bus_tot_inflight)
+		bus_gc();		/* Garbage collect fds */
+
+	return 0;
+}
+
+static void init_peercred(struct sock *sk)
+{
+	put_pid(sk->sk_peer_pid);
+	if (sk->sk_peer_cred)
+		put_cred(sk->sk_peer_cred);
+	sk->sk_peer_pid  = get_pid(task_tgid(current));
+	sk->sk_peer_cred = get_current_cred();
+}
+
+static void copy_peercred(struct sock *sk, struct sock *peersk)
+{
+	put_pid(sk->sk_peer_pid);
+	if (sk->sk_peer_cred)
+		put_cred(sk->sk_peer_cred);
+	sk->sk_peer_pid  = get_pid(peersk->sk_peer_pid);
+	sk->sk_peer_cred = get_cred(peersk->sk_peer_cred);
+}
+
+static int bus_listen(struct socket *sock, int backlog)
+{
+	int err;
+	struct sock *sk = sock->sk;
+	struct bus_sock *u = bus_sk(sk);
+	struct pid *old_pid = NULL;
+	const struct cred *old_cred = NULL;
+
+	err = -EINVAL;
+	if (!u->addr || !u->bus_master)
+		goto out;	/* Only listens on an bound an master socket */
+	bus_state_lock(sk);
+	if (sk->sk_state != BUS_CLOSE && sk->sk_state != BUS_LISTEN)
+		goto out_unlock;
+	if (backlog > sk->sk_max_ack_backlog)
+		wake_up_interruptible_all(&u->peer_wait);
+	sk->sk_max_ack_backlog	= backlog;
+	sk->sk_state		= BUS_LISTEN;
+	/* set credentials so connect can copy them */
+	init_peercred(sk);
+	err = 0;
+
+out_unlock:
+	bus_state_unlock(sk);
+	put_pid(old_pid);
+	if (old_cred)
+		put_cred(old_cred);
+out:
+	return err;
+}
+
+static int bus_release(struct socket *);
+static int bus_bind(struct socket *, struct sockaddr *, int);
+static int bus_connect(struct socket *, struct sockaddr *,
+			       int addr_len, int flags);
+static int bus_accept(struct socket *, struct socket *, int);
+static int bus_getname(struct socket *, struct sockaddr *, int *, int);
+static unsigned int bus_poll(struct file *, struct socket *,
+				    poll_table *);
+static int bus_ioctl(struct socket *, unsigned int, unsigned long);
+static int bus_shutdown(struct socket *, int);
+static int bus_setsockopt(struct socket *, int, int, char __user *,
+			   unsigned int);
+static int bus_sendmsg(struct kiocb *, struct socket *,
+		       struct msghdr *, size_t);
+static int bus_recvmsg(struct kiocb *, struct socket *,
+		       struct msghdr *, size_t, int);
+
+static void bus_set_peek_off(struct sock *sk, int val)
+{
+	struct bus_sock *u = bus_sk(sk);
+
+	mutex_lock(&u->readlock);
+	sk->sk_peek_off = val;
+	mutex_unlock(&u->readlock);
+}
+
+static const struct proto_ops bus_seqpacket_ops = {
+	.family =	PF_BUS,
+	.owner =	THIS_MODULE,
+	.release =	bus_release,
+	.bind =		bus_bind,
+	.connect =	bus_connect,
+	.socketpair =	sock_no_socketpair,
+	.accept =	bus_accept,
+	.getname =	bus_getname,
+	.poll =		bus_poll,
+	.ioctl =	bus_ioctl,
+	.listen =	bus_listen,
+	.shutdown =	bus_shutdown,
+	.setsockopt =	bus_setsockopt,
+	.getsockopt =	sock_no_getsockopt,
+	.sendmsg =	bus_sendmsg,
+	.recvmsg =	bus_recvmsg,
+	.mmap =		sock_no_mmap,
+	.sendpage =	sock_no_sendpage,
+	.set_peek_off =	bus_set_peek_off,
+};
+
+static struct proto bus_proto = {
+	.name			= "BUS",
+	.owner			= THIS_MODULE,
+	.obj_size		= sizeof(struct bus_sock),
+};
+
+/*
+ * AF_BUS sockets do not interact with hardware, hence they
+ * dont trigger interrupts - so it's safe for them to have
+ * bh-unsafe locking for their sk_receive_queue.lock. Split off
+ * this special lock-class by reinitializing the spinlock key:
+ */
+static struct lock_class_key af_bus_sk_receive_queue_lock_key;
+
+static struct sock *bus_create1(struct net *net, struct socket *sock)
+{
+	struct sock *sk = NULL;
+	struct bus_sock *u;
+
+	atomic_long_inc(&bus_nr_socks);
+	if (atomic_long_read(&bus_nr_socks) > 2 * get_max_files())
+		goto out;
+
+	sk = sk_alloc(net, PF_BUS, GFP_KERNEL, &bus_proto);
+	if (!sk)
+		goto out;
+
+	sock_init_data(sock, sk);
+	lockdep_set_class(&sk->sk_receive_queue.lock,
+				&af_bus_sk_receive_queue_lock_key);
+
+	sk->sk_write_space	= bus_write_space;
+	sk->sk_max_ack_backlog	= BUS_MAX_QLEN;
+	sk->sk_destruct		= bus_sock_destructor;
+	u	  = bus_sk(sk);
+	u->path.dentry = NULL;
+	u->path.mnt = NULL;
+	u->bus = NULL;
+	u->bus_master = false;
+	u->authenticated = false;
+	u->eavesdropper = false;
+	spin_lock_init(&u->lock);
+	atomic_long_set(&u->inflight, 0);
+	INIT_LIST_HEAD(&u->link);
+	INIT_HLIST_HEAD(&u->addr_list);
+	INIT_HLIST_NODE(&u->bus_node);
+	mutex_init(&u->readlock); /* single task reading lock */
+	init_waitqueue_head(&u->peer_wait);
+	bus_insert_socket(bus_sockets_unbound, sk);
+out:
+	if (sk == NULL)
+		atomic_long_dec(&bus_nr_socks);
+	else {
+		local_bh_disable();
+		sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1);
+		local_bh_enable();
+	}
+	return sk;
+}
+
+static int bus_create(struct net *net, struct socket *sock, int protocol,
+		       int kern)
+{
+	struct sock *sk;
+
+	if (protocol < BUS_PROTO_NONE || protocol > BUS_PROTO_DBUS)
+		return -EPROTONOSUPPORT;
+
+	if (protocol != BUS_PROTO_NONE)
+		request_module("net-pf-%d-proto-%d", PF_BUS, protocol);
+
+	sock->state = SS_UNCONNECTED;
+
+	if (sock->type == SOCK_SEQPACKET)
+		sock->ops = &bus_seqpacket_ops;
+	else
+		return -ESOCKTNOSUPPORT;
+
+	sk = bus_create1(net, sock);
+	if (!sk)
+		return -ENOMEM;
+
+	sk->sk_protocol = protocol;
+
+	return 0;
+}
+
+static int bus_release(struct socket *sock)
+{
+	struct sock *sk = sock->sk;
+
+	if (!sk)
+		return 0;
+
+	sock->sk = NULL;
+
+	return bus_release_sock(sk, 0);
+}
+
+static struct sock *bus_find_other(struct net *net,
+				   struct sockaddr_bus *sbusname, int len,
+				   int protocol, unsigned int hash, int *error)
+{
+	struct sock *u;
+	struct path path;
+	int err = 0;
+
+	if (sbusname->sbus_path[0]) {
+		struct inode *inode;
+		err = kern_path(sbusname->sbus_path, LOOKUP_FOLLOW, &path);
+		if (err)
+			goto fail;
+		inode = path.dentry->d_inode;
+		err = inode_permission(inode, MAY_WRITE);
+		if (err)
+			goto put_fail;
+
+		err = -ECONNREFUSED;
+		if (!S_ISSOCK(inode->i_mode))
+			goto put_fail;
+		u = bus_find_socket_byaddress(net, sbusname, len, protocol,
+					      hash);
+		if (!u)
+			goto put_fail;
+
+		touch_atime(&path);
+		path_put(&path);
+
+	} else {
+		err = -ECONNREFUSED;
+		u = bus_find_socket_byaddress(net, sbusname, len, protocol, hash);
+		if (u) {
+			struct dentry *dentry;
+			dentry = bus_sk(u)->path.dentry;
+			if (dentry)
+				touch_atime(&bus_sk(u)->path);
+		} else
+			goto fail;
+	}
+
+	return u;
+
+put_fail:
+	path_put(&path);
+fail:
+	*error = err;
+	return NULL;
+}
+
+
+static int bus_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
+{
+	struct sock *sk = sock->sk;
+	struct net *net = sock_net(sk);
+	struct bus_sock *u = bus_sk(sk);
+	struct sockaddr_bus *sbusaddr = (struct sockaddr_bus *)uaddr;
+	char *sbus_path = sbusaddr->sbus_path;
+	struct dentry *dentry = NULL;
+	struct path path;
+	int err;
+	unsigned int hash;
+	struct bus_address *addr;
+	struct hlist_head *list;
+	struct bus *bus;
+
+	err = -EINVAL;
+	if (sbusaddr->sbus_family != AF_BUS)
+		goto out;
+
+	/* If the address is available, the socket is the bus master */
+	sbusaddr->sbus_addr.s_addr = BUS_MASTER_ADDR;
+
+	err = bus_mkname(sbusaddr, addr_len, &hash);
+	if (err < 0)
+		goto out;
+	addr_len = err;
+
+	mutex_lock(&u->readlock);
+
+	err = -EINVAL;
+	if (u->addr)
+		goto out_up;
+
+	err = -ENOMEM;
+	addr = kzalloc(sizeof(*addr) + sizeof(struct sockaddr_bus), GFP_KERNEL);
+	if (!addr)
+		goto out_up;
+
+	memcpy(addr->name, sbusaddr, sizeof(struct sockaddr_bus));
+	addr->len = addr_len;
+	addr->hash = hash;
+	atomic_set(&addr->refcnt, 1);
+	addr->sock = sk;
+	INIT_HLIST_NODE(&addr->addr_node);
+	INIT_HLIST_NODE(&addr->table_node);
+
+	if (sbus_path[0]) {
+		umode_t mode;
+		err = 0;
+		/*
+		 * Get the parent directory, calculate the hash for last
+		 * component.
+		 */
+		dentry = kern_path_create(AT_FDCWD, sbus_path, &path, 0);
+		err = PTR_ERR(dentry);
+		if (IS_ERR(dentry))
+			goto out_mknod_parent;
+
+		/*
+		 * All right, let's create it.
+		 */
+		mode = S_IFSOCK |
+		       (SOCK_INODE(sock)->i_mode & ~current_umask());
+		err = mnt_want_write(path.mnt);
+		if (err)
+			goto out_mknod_dput;
+		err = security_path_mknod(&path, dentry, mode, 0);
+		if (err)
+			goto out_mknod_drop_write;
+		err = vfs_mknod(path.dentry->d_inode, dentry, mode, 0);
+out_mknod_drop_write:
+		mnt_drop_write(path.mnt);
+		if (err)
+			goto out_mknod_dput;
+		mutex_unlock(&path.dentry->d_inode->i_mutex);
+		dput(path.dentry);
+		path.dentry = dentry;
+	}
+
+	err = -ENOMEM;
+	bus = kzalloc(sizeof(*bus), GFP_KERNEL);
+	if (!bus)
+		goto out_unlock;
+
+	spin_lock(&bus_table_lock);
+
+	if (!sbus_path[0]) {
+		err = -EADDRINUSE;
+		if (__bus_find_socket_byname(net, sbusaddr, addr_len, hash)) {
+			bus_release_addr(addr);
+			kfree(bus);
+			goto out_unlock;
+		}
+
+		list = &bus_socket_table[addr->hash];
+	} else {
+		list = &bus_socket_table[dentry->d_inode->i_ino &
+					 (BUS_HASH_SIZE-1)];
+		u->path = path;
+	}
+
+	kref_init(&bus->kref);
+	bus->master = sk;
+	INIT_HLIST_HEAD(&bus->peers);
+	spin_lock_init(&bus->lock);
+	spin_lock_init(&bus->send_lock);
+	atomic64_set(&bus->addr_cnt, 0);
+	atomic64_set(&bus->eavesdropper_cnt, 0);
+
+	hlist_add_head(&addr->addr_node, &u->addr_list);
+
+	err = 0;
+	__bus_remove_socket(sk);
+	u->addr = addr;
+	u->bus_master = true;
+	u->bus = bus;
+	__bus_insert_socket(list, sk);
+	bus_insert_address(&bus_address_table[addr->hash], addr);
+
+out_unlock:
+	spin_unlock(&bus_table_lock);
+out_up:
+	mutex_unlock(&u->readlock);
+out:
+	return err;
+
+out_mknod_dput:
+	dput(dentry);
+	mutex_unlock(&path.dentry->d_inode->i_mutex);
+	path_put(&path);
+out_mknod_parent:
+	if (err == -EEXIST)
+		err = -EADDRINUSE;
+	bus_release_addr(addr);
+	goto out_up;
+}
+
+static long bus_wait_for_peer(struct sock *other, long timeo)
+{
+	struct bus_sock *u = bus_sk(other);
+	int sched;
+	DEFINE_WAIT(wait);
+
+	prepare_to_wait_exclusive(&u->peer_wait, &wait, TASK_INTERRUPTIBLE);
+
+	sched = !sock_flag(other, SOCK_DEAD) &&
+		!(other->sk_shutdown & RCV_SHUTDOWN) &&
+		bus_recvq_full(other);
+
+	bus_state_unlock(other);
+
+	if (sched)
+		timeo = schedule_timeout(timeo);
+
+	finish_wait(&u->peer_wait, &wait);
+	return timeo;
+}
+
+static int bus_connect(struct socket *sock, struct sockaddr *uaddr,
+			       int addr_len, int flags)
+{
+	struct sockaddr_bus *sbusaddr = (struct sockaddr_bus *)uaddr;
+	struct sock *sk = sock->sk;
+	struct net *net = sock_net(sk);
+	struct bus_sock *u = bus_sk(sk), *newu, *otheru;
+	struct sock *newsk = NULL;
+	struct sock *other = NULL;
+	struct sk_buff *skb = NULL;
+	struct bus_address *addr = NULL;
+	unsigned int hash;
+	int st;
+	int err;
+	long timeo;
+
+	/* Only connections to the bus master is allowed */
+	sbusaddr->sbus_addr.s_addr = BUS_MASTER_ADDR;
+
+	err = bus_mkname(sbusaddr, addr_len, &hash);
+	if (err < 0)
+		goto out;
+	addr_len = err;
+
+	err = -ENOMEM;
+	addr = kzalloc(sizeof(*addr) + sizeof(struct sockaddr_bus), GFP_KERNEL);
+	if (!addr)
+		goto out;
+
+	atomic_set(&addr->refcnt, 1);
+	INIT_HLIST_NODE(&addr->addr_node);
+	INIT_HLIST_NODE(&addr->table_node);
+
+	timeo = sock_sndtimeo(sk, flags & O_NONBLOCK);
+
+	/* First of all allocate resources.
+	   If we will make it after state is locked,
+	   we will have to recheck all again in any case.
+	 */
+
+	err = -ENOMEM;
+
+	/* create new sock for complete connection */
+	newsk = bus_create1(sock_net(sk), NULL);
+	if (newsk == NULL)
+		goto out;
+
+	/* Allocate skb for sending to listening sock */
+	skb = sock_wmalloc(newsk, 1, 0, GFP_KERNEL);
+	if (skb == NULL)
+		goto out;
+
+restart:
+	/*  Find listening sock. */
+	other = bus_find_other(net, sbusaddr, addr_len, sk->sk_protocol, hash,
+			       &err);
+	if (!other)
+		goto out;
+
+	/* Latch state of peer */
+	bus_state_lock(other);
+
+	/* Apparently VFS overslept socket death. Retry. */
+	if (sock_flag(other, SOCK_DEAD)) {
+		bus_state_unlock(other);
+		sock_put(other);
+		goto restart;
+	}
+
+	err = -ECONNREFUSED;
+	if (other->sk_state != BUS_LISTEN)
+		goto out_unlock;
+	if (other->sk_shutdown & RCV_SHUTDOWN)
+		goto out_unlock;
+
+	if (bus_recvq_full(other)) {
+		err = -EAGAIN;
+		if (!timeo)
+			goto out_unlock;
+
+		timeo = bus_wait_for_peer(other, timeo);
+
+		err = sock_intr_errno(timeo);
+		if (signal_pending(current))
+			goto out;
+		sock_put(other);
+		goto restart;
+	}
+
+	/* Latch our state.
+
+	   It is tricky place. We need to grab our state lock and cannot
+	   drop lock on peer. It is dangerous because deadlock is
+	   possible. Connect to self case and simultaneous
+	   attempt to connect are eliminated by checking socket
+	   state. other is BUS_LISTEN, if sk is BUS_LISTEN we
+	   check this before attempt to grab lock.
+
+	   Well, and we have to recheck the state after socket locked.
+	 */
+	st = sk->sk_state;
+
+	switch (st) {
+	case BUS_CLOSE:
+		/* This is ok... continue with connect */
+		break;
+	case BUS_ESTABLISHED:
+		/* Socket is already connected */
+		err = -EISCONN;
+		goto out_unlock;
+	default:
+		err = -EINVAL;
+		goto out_unlock;
+	}
+
+	bus_state_lock_nested(sk);
+
+	if (sk->sk_state != st) {
+		bus_state_unlock(sk);
+		bus_state_unlock(other);
+		sock_put(other);
+		goto restart;
+	}
+
+	err = security_bus_connect(sk, other, newsk);
+	if (err) {
+		bus_state_unlock(sk);
+		goto out_unlock;
+	}
+
+	/* The way is open! Fastly set all the necessary fields... */
+
+	sock_hold(sk);
+	bus_peer(newsk)	= sk;
+	newsk->sk_state		= BUS_ESTABLISHED;
+	newsk->sk_type		= sk->sk_type;
+	newsk->sk_protocol	= sk->sk_protocol;
+	init_peercred(newsk);
+	newu = bus_sk(newsk);
+	RCU_INIT_POINTER(newsk->sk_wq, &newu->peer_wq);
+	otheru = bus_sk(other);
+
+	/* copy address information from listening to new sock*/
+	if (otheru->addr && otheru->bus_master) {
+		atomic_inc(&otheru->addr->refcnt);
+		newu->addr = otheru->addr;
+		memcpy(addr->name, otheru->addr->name,
+		       sizeof(struct sockaddr_bus));
+		addr->len = otheru->addr->len;
+		addr->name->sbus_addr.s_addr =
+			(atomic64_inc_return(&otheru->bus->addr_cnt) &
+			 BUS_CLIENT_MASK);
+		addr->hash = bus_compute_hash(addr->name->sbus_addr);
+		addr->sock = sk;
+		u->addr = addr;
+		kref_get(&otheru->bus->kref);
+		u->bus = otheru->bus;
+		u->bus_master_side = false;
+		kref_get(&otheru->bus->kref);
+		newu->bus = otheru->bus;
+		newu->bus_master_side = true;
+		hlist_add_head(&addr->addr_node, &u->addr_list);
+
+		bus_insert_address(&bus_address_table[addr->hash], addr);
+	}
+	if (otheru->path.dentry) {
+		path_get(&otheru->path);
+		newu->path = otheru->path;
+	}
+
+	/* Set credentials */
+	copy_peercred(sk, other);
+	sk->sk_sndbuf = other->sk_sndbuf;
+	sk->sk_max_ack_backlog	= other->sk_max_ack_backlog;
+	newsk->sk_sndbuf = other->sk_sndbuf;
+
+	sock->state	= SS_CONNECTED;
+	sk->sk_state	= BUS_ESTABLISHED;
+	sock_hold(newsk);
+
+	smp_mb__after_atomic_inc();	/* sock_hold() does an atomic_inc() */
+	bus_peer(sk)	= newsk;
+
+	bus_state_unlock(sk);
+
+	/* take ten and and send info to listening sock */
+	spin_lock(&other->sk_receive_queue.lock);
+	__skb_queue_tail(&other->sk_receive_queue, skb);
+	spin_unlock(&other->sk_receive_queue.lock);
+	bus_state_unlock(other);
+	other->sk_data_ready(other, 0);
+	sock_put(other);
+	return 0;
+
+out_unlock:
+	if (other)
+		bus_state_unlock(other);
+
+out:
+	kfree_skb(skb);
+	if (addr)
+		bus_release_addr(addr);
+	if (newsk)
+		bus_release_sock(newsk, 0);
+	if (other)
+		sock_put(other);
+	return err;
+}
+
+static int bus_accept(struct socket *sock, struct socket *newsock, int flags)
+{
+	struct sock *sk = sock->sk;
+	struct sock *tsk;
+	struct sk_buff *skb;
+	int err;
+
+	err = -EINVAL;
+	if (sk->sk_state != BUS_LISTEN)
+		goto out;
+
+	/* If socket state is BUS_LISTEN it cannot change (for now...),
+	 * so that no locks are necessary.
+	 */
+
+	skb = skb_recv_datagram(sk, 0, flags&O_NONBLOCK, &err);
+	if (!skb) {
+		/* This means receive shutdown. */
+		if (err == 0)
+			err = -EINVAL;
+		goto out;
+	}
+
+	tsk = skb->sk;
+	skb_free_datagram(sk, skb);
+	wake_up_interruptible(&bus_sk(sk)->peer_wait);
+
+	/* attach accepted sock to socket */
+	bus_state_lock(tsk);
+	newsock->state = SS_CONNECTED;
+	sock_graft(tsk, newsock);
+	bus_state_unlock(tsk);
+	return 0;
+
+out:
+	return err;
+}
+
+
+static int bus_getname(struct socket *sock, struct sockaddr *uaddr,
+		       int *uaddr_len, int peer)
+{
+	struct sock *sk = sock->sk;
+	struct bus_sock *u;
+	DECLARE_SOCKADDR(struct sockaddr_bus *, sbusaddr, uaddr);
+	int err = 0;
+
+	if (peer) {
+		sk = bus_peer_get(sk);
+
+		err = -ENOTCONN;
+		if (!sk)
+			goto out;
+		err = 0;
+	} else {
+		sock_hold(sk);
+	}
+
+	u = bus_sk(sk);
+
+	bus_state_lock(sk);
+	if (!u->addr) {
+		sbusaddr->sbus_family = AF_BUS;
+		sbusaddr->sbus_path[0] = 0;
+		*uaddr_len = sizeof(short);
+	} else {
+		struct bus_address *addr = u->addr;
+
+		*uaddr_len = sizeof(struct sockaddr_bus);
+		memcpy(sbusaddr, addr->name, *uaddr_len);
+	}
+	bus_state_unlock(sk);
+	sock_put(sk);
+out:
+	return err;
+}
+
+static void bus_detach_fds(struct scm_cookie *scm, struct sk_buff *skb)
+{
+	int i;
+
+	scm->fp = BUSCB(skb).fp;
+	BUSCB(skb).fp = NULL;
+
+	for (i = scm->fp->count-1; i >= 0; i--)
+		bus_notinflight(scm->fp->fp[i]);
+}
+
+static void bus_destruct_scm(struct sk_buff *skb)
+{
+	struct scm_cookie scm;
+	memset(&scm, 0, sizeof(scm));
+	scm.pid  = BUSCB(skb).pid;
+	scm.cred = BUSCB(skb).cred;
+	if (BUSCB(skb).fp)
+		bus_detach_fds(&scm, skb);
+
+	scm_destroy(&scm);
+	if (skb->sk)
+		sock_wfree(skb);
+}
+
+#define MAX_RECURSION_LEVEL 4
+
+static int bus_attach_fds(struct scm_cookie *scm, struct sk_buff *skb)
+{
+	int i;
+	unsigned char max_level = 0;
+	int bus_sock_count = 0;
+
+	for (i = scm->fp->count - 1; i >= 0; i--) {
+		struct sock *sk = bus_get_socket(scm->fp->fp[i]);
+
+		if (sk) {
+			bus_sock_count++;
+			max_level = max(max_level,
+					bus_sk(sk)->recursion_level);
+		}
+	}
+	if (unlikely(max_level > MAX_RECURSION_LEVEL))
+		return -ETOOMANYREFS;
+
+	/*
+	 * Need to duplicate file references for the sake of garbage
+	 * collection.  Otherwise a socket in the fps might become a
+	 * candidate for GC while the skb is not yet queued.
+	 */
+	BUSCB(skb).fp = scm_fp_dup(scm->fp);
+	if (!BUSCB(skb).fp)
+		return -ENOMEM;
+
+	if (bus_sock_count) {
+		for (i = scm->fp->count - 1; i >= 0; i--)
+			bus_inflight(scm->fp->fp[i]);
+	}
+	return max_level;
+}
+
+static int bus_scm_to_skb(struct scm_cookie *scm, struct sk_buff *skb,
+			  bool send_fds)
+{
+	int err = 0;
+
+	BUSCB(skb).pid  = get_pid(scm->pid);
+	if (scm->cred)
+		BUSCB(skb).cred = get_cred(scm->cred);
+	BUSCB(skb).fp = NULL;
+	if (scm->fp && send_fds)
+		err = bus_attach_fds(scm, skb);
+
+	skb->destructor = bus_destruct_scm;
+	return err;
+}
+
+/*
+ * Some apps rely on write() giving SCM_CREDENTIALS
+ * We include credentials if source or destination socket
+ * asserted SOCK_PASSCRED.
+ */
+static void maybe_add_creds(struct sk_buff *skb, const struct socket *sock,
+			    const struct sock *other)
+{
+	if (BUSCB(skb).cred)
+		return;
+	if (test_bit(SOCK_PASSCRED, &sock->flags) ||
+	    !other->sk_socket ||
+	    test_bit(SOCK_PASSCRED, &other->sk_socket->flags)) {
+		BUSCB(skb).pid  = get_pid(task_tgid(current));
+		BUSCB(skb).cred = get_current_cred();
+	}
+}
+
+/*
+ *	Send AF_BUS data.
+ */
+
+static void bus_deliver_skb(struct sk_buff *skb)
+{
+	struct bus_send_context *sendctx = BUSCB(skb).sendctx;
+	struct socket *sock = sendctx->sender_socket;
+
+	if (sock_flag(sendctx->other, SOCK_RCVTSTAMP))
+		__net_timestamp(skb);
+	maybe_add_creds(skb, sock, sendctx->other);
+	skb_queue_tail(&sendctx->other->sk_receive_queue, skb);
+	if (sendctx->max_level > bus_sk(sendctx->other)->recursion_level)
+		bus_sk(sendctx->other)->recursion_level = sendctx->max_level;
+}
+
+/**
+ * bus_sendmsg_finish - delivery an skb to a destination
+ * @skb: sk_buff to deliver
+ *
+ * Delivers a packet to a destination. The skb control buffer has
+ * all the information about the destination contained on sending
+ * context. If the sending is unicast, then the skb is delivered
+ * and the receiver notified but if the sending is multicast, the
+ * skb is just marked as delivered and the actual delivery is made
+ * outside the function with the bus->send_lock held to ensure that
+ * the multicast sending is atomic.
+ */
+static int bus_sendmsg_finish(struct sk_buff *skb)
+{
+	int err;
+	struct bus_send_context *sendctx;
+	struct socket *sock;
+	struct sock *sk;
+	struct net *net;
+	size_t len = skb->len;
+
+	sendctx = BUSCB(skb).sendctx;
+	sock = sendctx->sender_socket;
+	sk = sock->sk;
+	net = sock_net(sk);
+
+restart:
+	if (!sendctx->other) {
+		err = -ECONNRESET;
+		if (sendctx->recipient == NULL)
+			goto out_free;
+
+		sendctx->other = bus_find_other(net, sendctx->recipient,
+						sendctx->namelen,
+						sk->sk_protocol,
+						sendctx->hash, &err);
+
+		if (sendctx->other == NULL ||
+		    !bus_sk(sendctx->other)->authenticated) {
+
+			if (sendctx->other)
+				sock_put(sendctx->other);
+
+			if (!bus_sk(sk)->bus_master_side) {
+				err = -ENOTCONN;
+				sendctx->other = bus_peer_get(sk);
+				if (!sendctx->other)
+					goto out_free;
+			} else {
+				sendctx->other = sk;
+				sock_hold(sendctx->other);
+			}
+		}
+	}
+
+	if (sk_filter(sendctx->other, skb) < 0) {
+		/* Toss the packet but do not return any error to the sender */
+		err = len;
+		goto out_free;
+	}
+
+	bus_state_lock(sendctx->other);
+
+	if (sock_flag(sendctx->other, SOCK_DEAD)) {
+		/*
+		 *	Check with 1003.1g - what should
+		 *	datagram error
+		 */
+		bus_state_unlock(sendctx->other);
+		sock_put(sendctx->other);
+
+		err = 0;
+		bus_state_lock(sk);
+		if (bus_peer(sk) == sendctx->other) {
+			bus_peer(sk) = NULL;
+			bus_state_unlock(sk);
+			sock_put(sendctx->other);
+			err = -ECONNREFUSED;
+		} else {
+			bus_state_unlock(sk);
+		}
+
+		sendctx->other = NULL;
+		if (err)
+			goto out_free;
+		goto restart;
+	}
+
+	err = -EPIPE;
+	if (sendctx->other->sk_shutdown & RCV_SHUTDOWN)
+		goto out_unlock;
+
+	if (bus_recvq_full(sendctx->other)) {
+		if (!sendctx->timeo) {
+			err = -EAGAIN;
+			goto out_unlock;
+		}
+
+		sendctx->timeo = bus_wait_for_peer(sendctx->other,
+						   sendctx->timeo);
+
+		err = sock_intr_errno(sendctx->timeo);
+		if (signal_pending(current))
+			goto out_free;
+
+		goto restart;
+	}
+
+	if (!sendctx->multicast && !sendctx->eavesdropper) {
+		bus_deliver_skb(skb);
+		bus_state_unlock(sendctx->other);
+		sendctx->other->sk_data_ready(sendctx->other, 0);
+		sock_put(sendctx->other);
+	} else {
+		sendctx->deliver = 1;
+		bus_state_unlock(sendctx->other);
+	}
+
+	return len;
+
+out_unlock:
+	bus_state_unlock(sendctx->other);
+out_free:
+	kfree_skb(skb);
+	if (sendctx->other)
+		sock_put(sendctx->other);
+
+	return err;
+}
+
+/**
+ * bus_sendmsg_mcast - do a multicast sending
+ * @skb: sk_buff to deliver
+ *
+ * Send a packet to a multicast destination.
+ * The function is also called for unicast sending when eavesdropping
+ * is enabled. Since the unicast destination and the eavesdroppers
+ * have to receive the packet atomically.
+ */
+static int bus_sendmsg_mcast(struct sk_buff *skb)
+{
+	struct bus_send_context *sendctx;
+	struct bus_send_context *tmpctx;
+	struct socket *sock;
+	struct sock *sk;
+	struct net *net;
+	struct bus_sock *u, *s;
+	struct hlist_node *node;
+	u16 prefix = 0;
+	struct sk_buff **skb_set = NULL;
+	struct bus_send_context **sendctx_set = NULL;
+	int  rcp_cnt, send_cnt;
+	int i;
+	int err;
+	int len = skb->len;
+	bool (*is_receiver) (struct sock *, u16);
+	bool main_rcp_found = false;
+
+	sendctx = BUSCB(skb).sendctx;
+	sendctx->deliver = 0;
+	sock = sendctx->sender_socket;
+	sk = sock->sk;
+	u = bus_sk(sk);
+	net = sock_net(sk);
+
+	if (sendctx->multicast) {
+		prefix = bus_addr_prefix(sendctx->recipient);
+		if (sendctx->eavesdropper)
+			is_receiver = &bus_has_prefix_eavesdropper;
+		else
+			is_receiver = &bus_has_prefix;
+	} else {
+		is_receiver = &bus_eavesdropper;
+
+		/*
+		 * If the destination is not the peer accepted socket
+		 * we have to get the correct destination.
+		 */
+		if (!sendctx->to_master && sendctx->recipient) {
+			sendctx->other = bus_find_other(net, sendctx->recipient,
+							sendctx->namelen,
+							sk->sk_protocol,
+							sendctx->hash, &err);
+
+
+			if (sendctx->other == NULL ||
+			    !bus_sk(sendctx->other)->authenticated) {
+
+				if (sendctx->other)
+					sock_put(sendctx->other);
+
+				if (sendctx->other == NULL) {
+					if (!bus_sk(sk)->bus_master_side) {
+						err = -ENOTCONN;
+						sendctx->other = bus_peer_get(sk);
+						if (!sendctx->other)
+							goto out;
+					} else {
+						sendctx->other = sk;
+						sock_hold(sendctx->other);
+					}
+				}
+				sendctx->to_master = 1;
+			}
+		}
+	}
+
+
+try_again:
+	rcp_cnt = 0;
+	main_rcp_found = false;
+
+	spin_lock(&u->bus->lock);
+
+	hlist_for_each_entry(s, node, &u->bus->peers, bus_node) {
+
+		if (!net_eq(sock_net(&s->sk), net))
+			continue;
+
+		if (is_receiver(&s->sk, prefix) ||
+		    (!sendctx->multicast &&
+		     !sendctx->to_master &&
+		     &s->sk == sendctx->other))
+			rcp_cnt++;
+	}
+
+	spin_unlock(&u->bus->lock);
+
+	/*
+	 * Memory can't be allocated while holding a spinlock so
+	 * we have to release the lock, do the allocation for the
+	 * array to store each destination peer sk_buff and grab
+	 * the bus peer lock again. Peers could have joined the
+	 * bus while we relesed the lock so we allocate 5 more
+	 * recipients hoping that this will be enough to not having
+	 * to try again in case only a few peers joined the bus.
+	 */
+	rcp_cnt += 5;
+	skb_set = kzalloc(sizeof(struct sk_buff *) * rcp_cnt, GFP_KERNEL);
+
+	if (!skb_set) {
+		err = -ENOMEM;
+		goto out;
+	}
+
+	sendctx_set = kzalloc(sizeof(struct bus_send_context *) * rcp_cnt,
+			      GFP_KERNEL);
+	if (!sendctx_set) {
+		err = -ENOMEM;
+		goto out;
+	}
+
+	for (i = 0; i < rcp_cnt; i++) {
+		skb_set[i] = skb_clone(skb, GFP_KERNEL);
+		if (!skb_set[i]) {
+			err = -ENOMEM;
+			goto out_free;
+		}
+		sendctx_set[i] = BUSCB(skb_set[i]).sendctx
+			= kmalloc(sizeof(*sendctx) * rcp_cnt, GFP_KERNEL);
+		if (!sendctx_set[i]) {
+			err = -ENOMEM;
+			goto out_free;
+		}
+		memcpy(sendctx_set[i], sendctx, sizeof(*sendctx));
+		err = bus_scm_to_skb(sendctx_set[i]->siocb->scm,
+				     skb_set[i], true);
+		if (err < 0)
+			goto out_free;
+		bus_get_secdata(sendctx_set[i]->siocb->scm,
+				skb_set[i]);
+
+		sendctx_set[i]->other = NULL;
+	}
+
+	send_cnt = 0;
+
+	spin_lock(&u->bus->lock);
+
+	hlist_for_each_entry(s, node, &u->bus->peers, bus_node) {
+
+		if (!net_eq(sock_net(&s->sk), net))
+			continue;
+
+		if (send_cnt >= rcp_cnt) {
+			spin_unlock(&u->bus->lock);
+
+			for (i = 0; i < rcp_cnt; i++) {
+				sock_put(sendctx_set[i]->other);
+				kfree_skb(skb_set[i]);
+				kfree(sendctx_set[i]);
+			}
+			kfree(skb_set);
+			kfree(sendctx_set);
+			sendctx_set = NULL;
+			skb_set = NULL;
+			goto try_again;
+		}
+
+		if (is_receiver(&s->sk, prefix) ||
+		    (!sendctx->multicast &&
+		     !sendctx->to_master &&
+		     &s->sk == sendctx->other)) {
+			skb_set_owner_w(skb_set[send_cnt], &s->sk);
+			tmpctx = BUSCB(skb_set[send_cnt]).sendctx;
+			sock_hold(&s->sk);
+			if (&s->sk == sendctx->other) {
+				tmpctx->main_recipient = 1;
+				main_rcp_found = true;
+			}
+			tmpctx->other = &s->sk;
+			tmpctx->recipient = s->addr->name;
+			tmpctx->eavesdropper = bus_eavesdropper(&s->sk, 0);
+
+			send_cnt++;
+		}
+	}
+
+	spin_unlock(&u->bus->lock);
+
+	/*
+	 * Peers have left the bus so we have to free
+	 * their pre-allocated bus_send_context and
+	 * socket buffers.
+	 */
+	if (send_cnt < rcp_cnt) {
+		for (i = send_cnt; i < rcp_cnt; i++) {
+			kfree_skb(skb_set[i]);
+			kfree(sendctx_set[i]);
+		}
+		rcp_cnt = send_cnt;
+	}
+
+	for (i = 0; i < send_cnt; i++) {
+		tmpctx = BUSCB(skb_set[i]).sendctx;
+		tmpctx->deliver = 0;
+		err = NF_HOOK(NFPROTO_BUS, NF_BUS_SENDING, skb_set[i],
+			      NULL, NULL, bus_sendmsg_finish);
+		if (err == -EPERM)
+			sock_put(tmpctx->other);
+	}
+
+	/*
+	 * If the send context is not multicast, the destination
+	 * coud be either the peer accepted socket descriptor or
+	 * a peer that is not an eavesdropper. If the peer is not
+	 * the accepted socket descriptor and has been authenticated,
+	 * it is a member of the bus peer list so it has already been
+	 * marked for delivery.
+	 * But if the destination is the accepted socket descriptor
+	 * or is a non-authenticated peer it is not a member of the
+	 * bus peer list so the packet has to be explicitly deliver
+	 * to it.
+	 */
+
+	if (!sendctx->multicast &&
+	    (sendctx->to_master ||
+	     (sendctx->bus_master_side && !main_rcp_found))) {
+		sendctx->main_recipient = 1;
+		err = NF_HOOK(NFPROTO_BUS, NF_BUS_SENDING, skb, NULL, NULL,
+			bus_sendmsg_finish);
+		if (err == -EPERM)
+			sock_put(sendctx->other);
+	}
+
+	spin_lock(&u->bus->send_lock);
+
+	for (i = 0; i < send_cnt; i++) {
+		tmpctx = sendctx_set[i];
+		if (tmpctx->deliver != 1)
+			continue;
+
+		bus_state_lock(tmpctx->other);
+		bus_deliver_skb(skb_set[i]);
+		bus_state_unlock(tmpctx->other);
+	}
+
+	if (!sendctx->multicast &&
+	    sendctx->deliver == 1 &&
+	    !bus_sk(sendctx->other)->eavesdropper) {
+		bus_state_lock(sendctx->other);
+		bus_deliver_skb(skb);
+		bus_state_unlock(sendctx->other);
+	}
+
+	spin_unlock(&u->bus->send_lock);
+
+	for (i = 0; i < send_cnt; i++) {
+		tmpctx = sendctx_set[i];
+		if (tmpctx->deliver != 1)
+			continue;
+
+		tmpctx->other->sk_data_ready(tmpctx->other, 0);
+		sock_put(tmpctx->other);
+	}
+
+	if (!sendctx->multicast &&
+	    sendctx->deliver == 1 &&
+	    !bus_sk(sendctx->other)->eavesdropper) {
+		sendctx->other->sk_data_ready(sendctx->other, 0);
+		sock_put(sendctx->other);
+	}
+
+	err = len;
+	goto out;
+
+out_free:
+	for (i = 0; i < rcp_cnt; i++) {
+		if (skb_set[i])
+			kfree_skb(skb_set[i]);
+	}
+
+out:
+	kfree(skb_set);
+	if (sendctx_set) {
+		for (i = 0; i < rcp_cnt; i++)
+			kfree(sendctx_set[i]);
+		kfree(sendctx_set);
+	}
+
+	if (sendctx->deliver == 0) {
+		if (!sendctx->to_master &&
+		    !(sendctx->bus_master_side && !main_rcp_found))
+			kfree_skb(skb);
+		if (!sendctx->to_master &&
+		    !(sendctx->bus_master_side && !main_rcp_found))
+			if (sendctx->other)
+				sock_put(sendctx->other);
+	}
+	scm_destroy(sendctx->siocb->scm);
+
+	return err;
+}
+
+/**
+ * bus_sendmsg - send an skb to a destination
+ * @kiocb: I/O control block info
+ * @sock: sender socket
+ * @msg: message header
+ * @len: message length
+ *
+ * Send an socket buffer to a destination. The destination could be
+ * either an unicast or a multicast address. In any case, a copy of
+ * the packet has to be send to all the sockets that are allowed to
+ * eavesdrop the communication bus.
+ *
+ * If the destination address is not associated with any socket, the
+ * packet is default routed to the bus master (the sender accepted
+ * socket).
+ *
+ * The af_bus sending path is hooked to the netfilter subsystem so
+ * netfilter hooks can filter or modify the packet before delivery.
+ */
+static int bus_sendmsg(struct kiocb *kiocb, struct socket *sock,
+				struct msghdr *msg, size_t len)
+{
+	struct sock *sk = sock->sk;
+	struct bus_sock *u = bus_sk(sk);
+	struct sockaddr_bus *sbusaddr = msg->msg_name;
+	int err;
+	struct sk_buff *skb;
+	struct scm_cookie tmp_scm;
+	bool to_master = false;
+	bool multicast = false;
+	struct bus_send_context sendctx;
+
+	err = sock_error(sk);
+	if (err)
+		return err;
+
+	if (sk->sk_state != BUS_ESTABLISHED)
+		return -ENOTCONN;
+
+	if (!msg->msg_namelen)
+		sbusaddr = NULL;
+
+	if (sbusaddr && !bus_same_bus(sbusaddr, u->addr->name))
+		return -EHOSTUNREACH;
+
+	if ((!sbusaddr && !u->bus_master_side) ||
+	    (sbusaddr && sbusaddr->sbus_addr.s_addr == BUS_MASTER_ADDR))
+		to_master = true;
+	else if (sbusaddr && !u->bus_master_side && !u->authenticated)
+		return -EHOSTUNREACH;
+
+	sendctx.namelen = 0; /* fake GCC */
+	sendctx.siocb = kiocb_to_siocb(kiocb);
+	sendctx.other = NULL;
+
+	if (NULL == sendctx.siocb->scm)
+		sendctx.siocb->scm = &tmp_scm;
+	wait_for_bus_gc();
+	err = scm_send(sock, msg, sendctx.siocb->scm);
+	if (err < 0)
+		return err;
+
+	err = -EOPNOTSUPP;
+	if (msg->msg_flags&MSG_OOB)
+		goto out;
+
+	if (sbusaddr && !to_master) {
+		err = bus_mkname(sbusaddr, msg->msg_namelen, &sendctx.hash);
+		if (err < 0)
+			goto out;
+		sendctx.namelen = err;
+		multicast = bus_mc_addr(sbusaddr);
+	} else {
+		err = -ENOTCONN;
+		sendctx.other = bus_peer_get(sk);
+		if (!sendctx.other)
+			goto out;
+	}
+
+	err = -EMSGSIZE;
+	if (len > sk->sk_sndbuf - 32)
+		goto out;
+
+	sendctx.timeo = sock_sndtimeo(sk, msg->msg_flags & MSG_DONTWAIT);
+
+restart:
+	bus_state_lock(sk);
+	if (bus_recvq_full(sk)) {
+		err = -EAGAIN;
+		if (!sendctx.timeo) {
+			bus_state_unlock(sk);
+			goto out;
+		}
+
+		sendctx.timeo = bus_wait_for_peer(sk, sendctx.timeo);
+
+		err = sock_intr_errno(sendctx.timeo);
+		if (signal_pending(current))
+			goto out;
+
+		goto restart;
+	} else {
+		bus_state_unlock(sk);
+	}
+
+	skb = sock_alloc_send_skb(sk, len, msg->msg_flags&MSG_DONTWAIT, &err);
+	if (skb == NULL)
+		goto out;
+
+	err = bus_scm_to_skb(sendctx.siocb->scm, skb, true);
+	if (err < 0)
+		goto out_free;
+	sendctx.max_level = err + 1;
+	bus_get_secdata(sendctx.siocb->scm, skb);
+
+	skb_reset_transport_header(skb);
+	err = memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len);
+	if (err)
+		goto out_free;
+
+	sendctx.sender_socket = sock;
+	if (u->bus_master_side && sendctx.other) {
+		/* if the bus master sent an unicast message to a peer, we
+		 * need the address of that peer
+		 */
+		sendctx.sender = bus_sk(sendctx.other)->addr->name;
+	} else {
+		sendctx.sender = u->addr->name;
+	}
+	sendctx.recipient = sbusaddr;
+	sendctx.authenticated = u->authenticated;
+	sendctx.bus_master_side = u->bus_master_side;
+	sendctx.to_master = to_master;
+	sendctx.multicast = multicast;
+	sendctx.eavesdropper = atomic64_read(&u->bus->eavesdropper_cnt) ? 1 : 0;
+	BUSCB(skb).sendctx = &sendctx;
+
+	if (sendctx.multicast || sendctx.eavesdropper) {
+		sendctx.main_recipient = 0;
+		err = bus_sendmsg_mcast(skb);
+		return sendctx.multicast ? len : err;
+	} else {
+		sendctx.main_recipient = 1;
+		len = NF_HOOK(NFPROTO_BUS, NF_BUS_SENDING, skb, NULL, NULL,
+			      bus_sendmsg_finish);
+
+		if (len == -EPERM) {
+			err = len;
+			goto out;
+		} else {
+			scm_destroy(sendctx.siocb->scm);
+			return len;
+		}
+	}
+
+out_free:
+	kfree_skb(skb);
+out:
+	if (sendctx.other)
+		sock_put(sendctx.other);
+	scm_destroy(sendctx.siocb->scm);
+	return err;
+}
+
+static void bus_copy_addr(struct msghdr *msg, struct sock *sk)
+{
+	struct bus_sock *u = bus_sk(sk);
+
+	msg->msg_namelen = 0;
+	if (u->addr) {
+		msg->msg_namelen = u->addr->len;
+		memcpy(msg->msg_name, u->addr->name,
+		       sizeof(struct sockaddr_bus));
+	}
+}
+
+static int bus_recvmsg(struct kiocb *iocb, struct socket *sock,
+			  struct msghdr *msg, size_t size, int flags)
+{
+	struct sock_iocb *siocb = kiocb_to_siocb(iocb);
+	struct scm_cookie tmp_scm;
+	struct sock *sk = sock->sk;
+	struct bus_sock *u = bus_sk(sk);
+	int noblock = flags & MSG_DONTWAIT;
+	struct sk_buff *skb;
+	int err;
+	int peeked, skip;
+
+	if (sk->sk_state != BUS_ESTABLISHED)
+		return -ENOTCONN;
+
+	err = -EOPNOTSUPP;
+	if (flags&MSG_OOB)
+		goto out;
+
+	msg->msg_namelen = 0;
+
+	err = mutex_lock_interruptible(&u->readlock);
+	if (err) {
+		err = sock_intr_errno(sock_rcvtimeo(sk, noblock));
+		goto out;
+	}
+
+	skip = sk_peek_offset(sk, flags);
+
+	skb = __skb_recv_datagram(sk, flags, &peeked, &skip, &err);
+	if (!skb) {
+		bus_state_lock(sk);
+		/* Signal EOF on disconnected non-blocking SEQPACKET socket. */
+		if (err == -EAGAIN && (sk->sk_shutdown & RCV_SHUTDOWN))
+			err = 0;
+		bus_state_unlock(sk);
+		goto out_unlock;
+	}
+
+	wake_up_interruptible_sync_poll(&u->peer_wait,
+					POLLOUT | POLLWRNORM | POLLWRBAND);
+
+	if (msg->msg_name)
+		bus_copy_addr(msg, skb->sk);
+
+	if (size > skb->len - skip)
+		size = skb->len - skip;
+	else if (size < skb->len - skip)
+		msg->msg_flags |= MSG_TRUNC;
+
+	err = skb_copy_datagram_iovec(skb, skip, msg->msg_iov, size);
+	if (err)
+		goto out_free;
+
+	if (sock_flag(sk, SOCK_RCVTSTAMP))
+		__sock_recv_timestamp(msg, sk, skb);
+
+	if (!siocb->scm) {
+		siocb->scm = &tmp_scm;
+		memset(&tmp_scm, 0, sizeof(tmp_scm));
+	}
+	scm_set_cred(siocb->scm, BUSCB(skb).pid, BUSCB(skb).cred);
+	bus_set_secdata(siocb->scm, skb);
+
+	if (!(flags & MSG_PEEK)) {
+		if (BUSCB(skb).fp)
+			bus_detach_fds(siocb->scm, skb);
+
+		sk_peek_offset_bwd(sk, skb->len);
+	} else {
+		/* It is questionable: on PEEK we could:
+		   - do not return fds - good, but too simple 8)
+		   - return fds, and do not return them on read (old strategy,
+		     apparently wrong)
+		   - clone fds (I chose it for now, it is the most universal
+		     solution)
+
+		   POSIX 1003.1g does not actually define this clearly
+		   at all. POSIX 1003.1g doesn't define a lot of things
+		   clearly however!
+
+		*/
+
+		sk_peek_offset_fwd(sk, size);
+
+		if (BUSCB(skb).fp)
+			siocb->scm->fp = scm_fp_dup(BUSCB(skb).fp);
+	}
+	err = (flags & MSG_TRUNC) ? skb->len - skip : size;
+
+	scm_recv(sock, msg, siocb->scm, flags);
+
+out_free:
+	skb_free_datagram(sk, skb);
+out_unlock:
+	mutex_unlock(&u->readlock);
+out:
+	return err;
+}
+
+static int bus_shutdown(struct socket *sock, int mode)
+{
+	struct sock *sk = sock->sk;
+	struct sock *other;
+
+	mode = (mode+1)&(RCV_SHUTDOWN|SEND_SHUTDOWN);
+
+	if (!mode)
+		return 0;
+
+	bus_state_lock(sk);
+	sk->sk_shutdown |= mode;
+	other = bus_peer(sk);
+	if (other)
+		sock_hold(other);
+	bus_state_unlock(sk);
+	sk->sk_state_change(sk);
+
+	if (other) {
+
+		int peer_mode = 0;
+
+		if (mode&RCV_SHUTDOWN)
+			peer_mode |= SEND_SHUTDOWN;
+		if (mode&SEND_SHUTDOWN)
+			peer_mode |= RCV_SHUTDOWN;
+		bus_state_lock(other);
+		other->sk_shutdown |= peer_mode;
+		bus_state_unlock(other);
+		other->sk_state_change(other);
+		if (peer_mode == SHUTDOWN_MASK)
+			sk_wake_async(other, SOCK_WAKE_WAITD, POLL_HUP);
+		else if (peer_mode & RCV_SHUTDOWN)
+			sk_wake_async(other, SOCK_WAKE_WAITD, POLL_IN);
+		sock_put(other);
+	}
+
+	return 0;
+}
+
+static int bus_add_addr(struct sock *sk, struct bus_addr *sbus_addr)
+{
+	struct bus_address *addr;
+	struct sock *other;
+	struct bus_sock *u = bus_sk(sk);
+	struct net *net = sock_net(sk);
+	int ret = 0;
+
+	addr = kzalloc(sizeof(*addr) + sizeof(struct sockaddr_bus), GFP_KERNEL);
+	if (!addr) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	memcpy(addr->name, u->addr->name, sizeof(struct sockaddr_bus));
+	addr->len = u->addr->len;
+
+	addr->name->sbus_addr.s_addr = sbus_addr->s_addr;
+	addr->hash = bus_compute_hash(addr->name->sbus_addr);
+	other = bus_find_socket_byaddress(net, addr->name, addr->len,
+					  sk->sk_protocol, addr->hash);
+
+	if (other) {
+		sock_put(other);
+		kfree(addr);
+		ret = -EADDRINUSE;
+		goto out;
+	}
+
+	atomic_set(&addr->refcnt, 1);
+	INIT_HLIST_NODE(&addr->addr_node);
+	INIT_HLIST_NODE(&addr->table_node);
+
+	addr->sock = sk;
+
+	hlist_add_head(&addr->addr_node, &u->addr_list);
+	bus_insert_address(&bus_address_table[addr->hash], addr);
+
+out:
+	sock_put(sk);
+
+	return ret;
+}
+
+static int bus_del_addr(struct sock *sk, struct bus_addr *sbus_addr)
+{
+	struct bus_address *addr;
+	int ret = 0;
+
+	bus_state_lock(sk);
+	addr = __bus_get_address(sk, sbus_addr);
+	if (!addr) {
+		ret = -EINVAL;
+		bus_state_unlock(sk);
+		goto out;
+	}
+	hlist_del(&addr->addr_node);
+	bus_state_unlock(sk);
+
+	bus_remove_address(addr);
+	bus_release_addr(addr);
+out:
+	sock_put(sk);
+
+	return ret;
+}
+
+static int bus_join_bus(struct sock *sk)
+{
+	struct sock *peer;
+	struct bus_sock *u = bus_sk(sk), *peeru;
+	int err = 0;
+
+	peer = bus_peer_get(sk);
+	if (!peer)
+		return -ENOTCONN;
+	peeru = bus_sk(peer);
+
+	if (!u->bus_master_side || peeru->authenticated) {
+		err = -EINVAL;
+		goto sock_put_out;
+	}
+
+	if (sk->sk_state != BUS_ESTABLISHED) {
+		err = -ENOTCONN;
+		goto sock_put_out;
+	}
+
+	if (peer->sk_shutdown != 0) {
+		err = -ENOTCONN;
+		goto sock_put_out;
+	}
+
+	bus_state_lock(peer);
+	peeru->authenticated = true;
+	bus_state_unlock(peer);
+
+	spin_lock(&u->bus->lock);
+	hlist_add_head(&peeru->bus_node, &u->bus->peers);
+	spin_unlock(&u->bus->lock);
+
+sock_put_out:
+	sock_put(peer);
+	return err;
+}
+
+static int __bus_set_eavesdrop(struct sock *sk, bool eavesdrop)
+{
+	struct sock *peer = bus_peer_get(sk);
+	struct bus_sock *u = bus_sk(sk), *peeru;
+	int err = 0;
+
+	if (!peer)
+		return -ENOTCONN;
+
+	if (sk->sk_state != BUS_ESTABLISHED) {
+		err = -ENOTCONN;
+		goto sock_put_out;
+	}
+
+	peeru = bus_sk(peer);
+
+	if (!u->bus_master_side || !peeru->authenticated) {
+		err = -EINVAL;
+		goto sock_put_out;
+	}
+
+	if (peer->sk_shutdown != 0) {
+		err = -ENOTCONN;
+		goto sock_put_out;
+	}
+
+	bus_state_lock(peeru);
+	if (peeru->eavesdropper != eavesdrop) {
+		peeru->eavesdropper = eavesdrop;
+		if (eavesdrop)
+			atomic64_inc(&u->bus->eavesdropper_cnt);
+		else
+			atomic64_dec(&u->bus->eavesdropper_cnt);
+	}
+	bus_state_unlock(peeru);
+
+sock_put_out:
+	sock_put(peer);
+	return err;
+}
+
+static int bus_set_eavesdrop(struct sock *sk)
+{
+	return __bus_set_eavesdrop(sk, true);
+}
+
+static int bus_unset_eavesdrop(struct sock *sk)
+{
+	return __bus_set_eavesdrop(sk, false);
+}
+
+static inline void sk_sendbuf_set(struct sock *sk, int sndbuf)
+{
+	bus_state_lock(sk);
+	sk->sk_sndbuf = sndbuf;
+	bus_state_unlock(sk);
+}
+
+static inline void sk_maxqlen_set(struct sock *sk, int qlen)
+{
+	bus_state_lock(sk);
+	sk->sk_max_ack_backlog = qlen;
+	bus_state_unlock(sk);
+}
+
+static int bus_setsockopt(struct socket *sock, int level, int optname,
+			   char __user *optval, unsigned int optlen)
+{
+	struct bus_addr addr;
+	int res;
+	int val;
+
+	if (level != SOL_BUS)
+		return -ENOPROTOOPT;
+
+	switch (optname) {
+	case BUS_ADD_ADDR:
+	case BUS_DEL_ADDR:
+		if (optlen < sizeof(struct bus_addr))
+			return -EINVAL;
+
+		if (!bus_sk(sock->sk)->bus_master_side)
+			return -EINVAL;
+
+		if (copy_from_user(&addr, optval, sizeof(struct bus_addr)))
+			return -EFAULT;
+
+		if (optname == BUS_ADD_ADDR)
+			res = bus_add_addr(bus_peer_get(sock->sk), &addr);
+		else
+			res = bus_del_addr(bus_peer_get(sock->sk), &addr);
+		break;
+	case BUS_JOIN_BUS:
+		res = bus_join_bus(sock->sk);
+		break;
+	case BUS_SET_EAVESDROP:
+		res = bus_set_eavesdrop(sock->sk);
+		break;
+	case BUS_UNSET_EAVESDROP:
+		res = bus_unset_eavesdrop(sock->sk);
+		break;
+	case BUS_SET_SENDBUF:
+	case BUS_SET_MAXQLEN:
+		if (sock->sk->sk_state != BUS_LISTEN) {
+			res = -EINVAL;
+		} else {
+			res = -EFAULT;
+
+			if (copy_from_user(&val, optval, optlen))
+				break;
+
+			res = 0;
+
+			if (optname == BUS_SET_SENDBUF)
+				sk_sendbuf_set(sock->sk, val);
+			else
+				sk_maxqlen_set(sock->sk, val);
+		}
+		break;
+	default:
+		res = -EINVAL;
+		break;
+	}
+
+	return res;
+}
+
+long bus_inq_len(struct sock *sk)
+{
+	struct sk_buff *skb;
+	long amount = 0;
+
+	if (sk->sk_state == BUS_LISTEN)
+		return -EINVAL;
+
+	spin_lock(&sk->sk_receive_queue.lock);
+	skb_queue_walk(&sk->sk_receive_queue, skb)
+		amount += skb->len;
+	spin_unlock(&sk->sk_receive_queue.lock);
+
+	return amount;
+}
+EXPORT_SYMBOL_GPL(bus_inq_len);
+
+long bus_outq_len(struct sock *sk)
+{
+	return sk_wmem_alloc_get(sk);
+}
+EXPORT_SYMBOL_GPL(bus_outq_len);
+
+static int bus_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
+{
+	struct sock *sk = sock->sk;
+	long amount = 0;
+	int err;
+
+	switch (cmd) {
+	case SIOCOUTQ:
+		amount = bus_outq_len(sk);
+		err = put_user(amount, (int __user *)arg);
+		break;
+	case SIOCINQ:
+		amount = bus_inq_len(sk);
+		if (amount < 0)
+			err = amount;
+		else
+			err = put_user(amount, (int __user *)arg);
+		break;
+	default:
+		err = -ENOIOCTLCMD;
+		break;
+	}
+	return err;
+}
+
+static unsigned int bus_poll(struct file *file, struct socket *sock,
+				    poll_table *wait)
+{
+	struct sock *sk = sock->sk, *other;
+	unsigned int mask, writable;
+	struct bus_sock *u = bus_sk(sk), *p;
+	struct hlist_node *node;
+
+	sock_poll_wait(file, sk_sleep(sk), wait);
+	mask = 0;
+
+	/* exceptional events? */
+	if (sk->sk_err || !skb_queue_empty(&sk->sk_error_queue))
+		mask |= POLLERR;
+	if (sk->sk_shutdown & RCV_SHUTDOWN)
+		mask |= POLLRDHUP | POLLIN | POLLRDNORM;
+	if (sk->sk_shutdown == SHUTDOWN_MASK)
+		mask |= POLLHUP;
+
+	/* readable? */
+	if (!skb_queue_empty(&sk->sk_receive_queue))
+		mask |= POLLIN | POLLRDNORM;
+
+	/* Connection-based need to check for termination and startup */
+	if (sk->sk_state == BUS_CLOSE)
+		mask |= POLLHUP;
+
+	/* No write status requested, avoid expensive OUT tests. */
+	if (!(poll_requested_events(wait) & (POLLWRBAND|POLLWRNORM|POLLOUT)))
+		return mask;
+
+	writable = bus_writable(sk);
+	other = bus_peer_get(sk);
+	if (other) {
+		if (bus_recvq_full(other))
+			writable = 0;
+		sock_put(other);
+	}
+
+	/*
+	 * If the socket has already joined the bus we have to check
+	 * that each peer receiver queue on the bus is not full.
+	 */
+	if (!u->bus_master_side && u->authenticated) {
+		spin_lock(&u->bus->lock);
+		hlist_for_each_entry(p, node, &u->bus->peers, bus_node) {
+			if (bus_recvq_full(&p->sk)) {
+				writable = 0;
+				break;
+			}
+		}
+		spin_unlock(&u->bus->lock);
+	}
+
+	if (writable)
+		mask |= POLLOUT | POLLWRNORM | POLLWRBAND;
+	else
+		set_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags);
+
+	return mask;
+}
+
+#ifdef CONFIG_PROC_FS
+static struct sock *first_bus_socket(int *i)
+{
+	for (*i = 0; *i <= BUS_HASH_SIZE; (*i)++) {
+		if (!hlist_empty(&bus_socket_table[*i]))
+			return __sk_head(&bus_socket_table[*i]);
+	}
+	return NULL;
+}
+
+static struct sock *next_bus_socket(int *i, struct sock *s)
+{
+	struct sock *next = sk_next(s);
+	/* More in this chain? */
+	if (next)
+		return next;
+	/* Look for next non-empty chain. */
+	for ((*i)++; *i <= BUS_HASH_SIZE; (*i)++) {
+		if (!hlist_empty(&bus_socket_table[*i]))
+			return __sk_head(&bus_socket_table[*i]);
+	}
+	return NULL;
+}
+
+struct bus_iter_state {
+	struct seq_net_private p;
+	int i;
+};
+
+static struct sock *bus_seq_idx(struct seq_file *seq, loff_t pos)
+{
+	struct bus_iter_state *iter = seq->private;
+	loff_t off = 0;
+	struct sock *s;
+
+	for (s = first_bus_socket(&iter->i); s;
+	     s = next_bus_socket(&iter->i, s)) {
+		if (sock_net(s) != seq_file_net(seq))
+			continue;
+		if (off == pos)
+			return s;
+		++off;
+	}
+	return NULL;
+}
+
+static void *bus_seq_start(struct seq_file *seq, loff_t *pos)
+	__acquires(bus_table_lock)
+{
+	spin_lock(&bus_table_lock);
+	return *pos ? bus_seq_idx(seq, *pos - 1) : SEQ_START_TOKEN;
+}
+
+static void *bus_seq_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+	struct bus_iter_state *iter = seq->private;
+	struct sock *sk = v;
+	++*pos;
+
+	if (v == SEQ_START_TOKEN)
+		sk = first_bus_socket(&iter->i);
+	else
+		sk = next_bus_socket(&iter->i, sk);
+	while (sk && (sock_net(sk) != seq_file_net(seq)))
+		sk = next_bus_socket(&iter->i, sk);
+	return sk;
+}
+
+static void bus_seq_stop(struct seq_file *seq, void *v)
+	__releases(bus_table_lock)
+{
+	spin_unlock(&bus_table_lock);
+}
+
+static int bus_seq_show(struct seq_file *seq, void *v)
+{
+
+	if (v == SEQ_START_TOKEN)
+		seq_puts(seq, "Num       RefCount Protocol Flags    Type St " \
+			 "Inode Path\n");
+	else {
+		struct sock *s = v;
+		struct bus_sock *u = bus_sk(s);
+		bus_state_lock(s);
+
+		seq_printf(seq, "%pK: %08X %08X %08X %04X %02X %5lu",
+			s,
+			atomic_read(&s->sk_refcnt),
+			0,
+			s->sk_state == BUS_LISTEN ? __SO_ACCEPTCON : 0,
+			s->sk_type,
+			s->sk_socket ?
+			(s->sk_state == BUS_ESTABLISHED ? SS_CONNECTED : SS_UNCONNECTED) :
+			(s->sk_state == BUS_ESTABLISHED ? SS_CONNECTING : SS_DISCONNECTING),
+			sock_i_ino(s));
+
+		if (u->addr) {
+			int i, len;
+			seq_putc(seq, ' ');
+
+			i = 0;
+			len = u->addr->len - sizeof(short);
+			if (!BUS_ABSTRACT(s))
+				len--;
+			else {
+				seq_putc(seq, '@');
+				i++;
+			}
+			for ( ; i < len; i++)
+				seq_putc(seq, u->addr->name->sbus_path[i]);
+		}
+		bus_state_unlock(s);
+		seq_putc(seq, '\n');
+	}
+
+	return 0;
+}
+
+static const struct seq_operations bus_seq_ops = {
+	.start  = bus_seq_start,
+	.next   = bus_seq_next,
+	.stop   = bus_seq_stop,
+	.show   = bus_seq_show,
+};
+
+static int bus_seq_open(struct inode *inode, struct file *file)
+{
+	return seq_open_net(inode, file, &bus_seq_ops,
+			    sizeof(struct bus_iter_state));
+}
+
+static const struct file_operations bus_seq_fops = {
+	.owner		= THIS_MODULE,
+	.open		= bus_seq_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= seq_release_net,
+};
+
+#endif
+
+static const struct net_proto_family bus_family_ops = {
+	.family = PF_BUS,
+	.create = bus_create,
+	.owner	= THIS_MODULE,
+};
+
+static int __init af_bus_init(void)
+{
+	int rc = -1;
+	struct sk_buff *dummy_skb;
+
+	BUILD_BUG_ON(sizeof(struct bus_skb_parms) > sizeof(dummy_skb->cb));
+
+	rc = proto_register(&bus_proto, 1);
+	if (rc != 0) {
+		pr_crit("%s: Cannot create bus_sock SLAB cache!\n", __func__);
+		return rc;
+	}
+
+	sock_register(&bus_family_ops);
+	return rc;
+}
+
+static void __exit af_bus_exit(void)
+{
+	sock_unregister(PF_BUS);
+	proto_unregister(&bus_proto);
+}
+
+module_init(af_bus_init);
+module_exit(af_bus_exit);
+
+MODULE_AUTHOR("Alban Crequy, Javier Martinez Canillas");
+MODULE_DESCRIPTION("Linux Bus domain sockets");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_NETPROTO(PF_BUS);
-- 
1.7.10

^ permalink raw reply related

* [PATCH net-next 09/15] net: bus: Add garbage collector for AF_BUS sockets.
From: Vincent Sanders @ 2012-06-29 16:45 UTC (permalink / raw)
  To: netdev, linux-kernel, David S. Miller
  Cc: Javier Martinez Canillas, Vincent Sanders
In-Reply-To: <1340988354-26981-1-git-send-email-vincent.sanders@collabora.co.uk>

From: Javier Martinez Canillas <javier.martinez@collabora.co.uk>

This patch adds a garbage collector for AF_BUS sockets.

Signed-off-by: Javier Martinez Canillas <javier.martinez@collabora.co.uk>
Signed-off-by: Vincent Sanders <vincent.sanders@collabora.co.uk>
---
 net/bus/garbage.c |  322 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 322 insertions(+)
 create mode 100644 net/bus/garbage.c

diff --git a/net/bus/garbage.c b/net/bus/garbage.c
new file mode 100644
index 0000000..2435f38
--- /dev/null
+++ b/net/bus/garbage.c
@@ -0,0 +1,322 @@
+/*
+ * Garbage Collector For AF_BUS sockets
+ *
+ * Based on Garbage Collector For AF_UNIX sockets (net/unix/garbage.c).
+ */
+
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/socket.h>
+#include <linux/un.h>
+#include <linux/net.h>
+#include <linux/fs.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/file.h>
+#include <linux/proc_fs.h>
+#include <linux/mutex.h>
+#include <linux/wait.h>
+
+#include <net/sock.h>
+#include <net/af_bus.h>
+#include <net/scm.h>
+#include <net/tcp_states.h>
+
+/* Internal data structures and random procedures: */
+
+static LIST_HEAD(gc_inflight_list);
+static LIST_HEAD(gc_candidates);
+static DEFINE_SPINLOCK(bus_gc_lock);
+static DECLARE_WAIT_QUEUE_HEAD(bus_gc_wait);
+
+unsigned int bus_tot_inflight;
+
+
+struct sock *bus_get_socket(struct file *filp)
+{
+	struct sock *u_sock = NULL;
+	struct inode *inode = filp->f_path.dentry->d_inode;
+
+	/*
+	 *	Socket ?
+	 */
+	if (S_ISSOCK(inode->i_mode) && !(filp->f_mode & FMODE_PATH)) {
+		struct socket *sock = SOCKET_I(inode);
+		struct sock *s = sock->sk;
+
+		/*
+		 *	PF_BUS ?
+		 */
+		if (s && sock->ops && sock->ops->family == PF_BUS)
+			u_sock = s;
+	}
+	return u_sock;
+}
+
+/*
+ *	Keep the number of times in flight count for the file
+ *	descriptor if it is for an AF_BUS socket.
+ */
+
+void bus_inflight(struct file *fp)
+{
+	struct sock *s = bus_get_socket(fp);
+	if (s) {
+		struct bus_sock *u = bus_sk(s);
+		spin_lock(&bus_gc_lock);
+		if (atomic_long_inc_return(&u->inflight) == 1) {
+			BUG_ON(!list_empty(&u->link));
+			list_add_tail(&u->link, &gc_inflight_list);
+		} else {
+			BUG_ON(list_empty(&u->link));
+		}
+		bus_tot_inflight++;
+		spin_unlock(&bus_gc_lock);
+	}
+}
+
+void bus_notinflight(struct file *fp)
+{
+	struct sock *s = bus_get_socket(fp);
+	if (s) {
+		struct bus_sock *u = bus_sk(s);
+		spin_lock(&bus_gc_lock);
+		BUG_ON(list_empty(&u->link));
+		if (atomic_long_dec_and_test(&u->inflight))
+			list_del_init(&u->link);
+		bus_tot_inflight--;
+		spin_unlock(&bus_gc_lock);
+	}
+}
+
+static void scan_inflight(struct sock *x, void (*func)(struct bus_sock *),
+			  struct sk_buff_head *hitlist)
+{
+	struct sk_buff *skb;
+	struct sk_buff *next;
+
+	spin_lock(&x->sk_receive_queue.lock);
+	skb_queue_walk_safe(&x->sk_receive_queue, skb, next) {
+		/*
+		 *	Do we have file descriptors ?
+		 */
+		if (BUSCB(skb).fp) {
+			bool hit = false;
+			/*
+			 *	Process the descriptors of this socket
+			 */
+			int nfd = BUSCB(skb).fp->count;
+			struct file **fp = BUSCB(skb).fp->fp;
+			while (nfd--) {
+				/*
+				 *	Get the socket the fd matches
+				 *	if it indeed does so
+				 */
+				struct sock *sk = bus_get_socket(*fp++);
+				if (sk) {
+					struct bus_sock *u = bus_sk(sk);
+
+					/*
+					 * Ignore non-candidates, they could
+					 * have been added to the queues after
+					 * starting the garbage collection
+					 */
+					if (u->gc_candidate) {
+						hit = true;
+						func(u);
+					}
+				}
+			}
+			if (hit && hitlist != NULL) {
+				__skb_unlink(skb, &x->sk_receive_queue);
+				__skb_queue_tail(hitlist, skb);
+			}
+		}
+	}
+	spin_unlock(&x->sk_receive_queue.lock);
+}
+
+static void scan_children(struct sock *x, void (*func)(struct bus_sock *),
+			  struct sk_buff_head *hitlist)
+{
+	if (x->sk_state != TCP_LISTEN)
+		scan_inflight(x, func, hitlist);
+	else {
+		struct sk_buff *skb;
+		struct sk_buff *next;
+		struct bus_sock *u;
+		LIST_HEAD(embryos);
+
+		/*
+		 * For a listening socket collect the queued embryos
+		 * and perform a scan on them as well.
+		 */
+		spin_lock(&x->sk_receive_queue.lock);
+		skb_queue_walk_safe(&x->sk_receive_queue, skb, next) {
+			u = bus_sk(skb->sk);
+
+			/*
+			 * An embryo cannot be in-flight, so it's safe
+			 * to use the list link.
+			 */
+			BUG_ON(!list_empty(&u->link));
+			list_add_tail(&u->link, &embryos);
+		}
+		spin_unlock(&x->sk_receive_queue.lock);
+
+		while (!list_empty(&embryos)) {
+			u = list_entry(embryos.next, struct bus_sock, link);
+			scan_inflight(&u->sk, func, hitlist);
+			list_del_init(&u->link);
+		}
+	}
+}
+
+static void dec_inflight(struct bus_sock *usk)
+{
+	atomic_long_dec(&usk->inflight);
+}
+
+static void inc_inflight(struct bus_sock *usk)
+{
+	atomic_long_inc(&usk->inflight);
+}
+
+static void inc_inflight_move_tail(struct bus_sock *u)
+{
+	atomic_long_inc(&u->inflight);
+	/*
+	 * If this still might be part of a cycle, move it to the end
+	 * of the list, so that it's checked even if it was already
+	 * passed over
+	 */
+	if (u->gc_maybe_cycle)
+		list_move_tail(&u->link, &gc_candidates);
+}
+
+static bool gc_in_progress = false;
+#define BUS_INFLIGHT_TRIGGER_GC 16000
+
+void wait_for_bus_gc(void)
+{
+	/*
+	 * If number of inflight sockets is insane,
+	 * force a garbage collect right now.
+	 */
+	if (bus_tot_inflight > BUS_INFLIGHT_TRIGGER_GC && !gc_in_progress)
+		bus_gc();
+	wait_event(bus_gc_wait, gc_in_progress == false);
+}
+
+/* The external entry point: bus_gc() */
+void bus_gc(void)
+{
+	struct bus_sock *u;
+	struct bus_sock *next;
+	struct sk_buff_head hitlist;
+	struct list_head cursor;
+	LIST_HEAD(not_cycle_list);
+
+	spin_lock(&bus_gc_lock);
+
+	/* Avoid a recursive GC. */
+	if (gc_in_progress)
+		goto out;
+
+	gc_in_progress = true;
+	/*
+	 * First, select candidates for garbage collection.  Only
+	 * in-flight sockets are considered, and from those only ones
+	 * which don't have any external reference.
+	 *
+	 * Holding bus_gc_lock will protect these candidates from
+	 * being detached, and hence from gaining an external
+	 * reference.  Since there are no possible receivers, all
+	 * buffers currently on the candidates' queues stay there
+	 * during the garbage collection.
+	 *
+	 * We also know that no new candidate can be added onto the
+	 * receive queues.  Other, non candidate sockets _can_ be
+	 * added to queue, so we must make sure only to touch
+	 * candidates.
+	 */
+	list_for_each_entry_safe(u, next, &gc_inflight_list, link) {
+		long total_refs;
+		long inflight_refs;
+
+		total_refs = file_count(u->sk.sk_socket->file);
+		inflight_refs = atomic_long_read(&u->inflight);
+
+		BUG_ON(inflight_refs < 1);
+		BUG_ON(total_refs < inflight_refs);
+		if (total_refs == inflight_refs) {
+			list_move_tail(&u->link, &gc_candidates);
+			u->gc_candidate = 1;
+			u->gc_maybe_cycle = 1;
+		}
+	}
+
+	/*
+	 * Now remove all internal in-flight reference to children of
+	 * the candidates.
+	 */
+	list_for_each_entry(u, &gc_candidates, link)
+		scan_children(&u->sk, dec_inflight, NULL);
+
+	/*
+	 * Restore the references for children of all candidates,
+	 * which have remaining references.  Do this recursively, so
+	 * only those remain, which form cyclic references.
+	 *
+	 * Use a "cursor" link, to make the list traversal safe, even
+	 * though elements might be moved about.
+	 */
+	list_add(&cursor, &gc_candidates);
+	while (cursor.next != &gc_candidates) {
+		u = list_entry(cursor.next, struct bus_sock, link);
+
+		/* Move cursor to after the current position. */
+		list_move(&cursor, &u->link);
+
+		if (atomic_long_read(&u->inflight) > 0) {
+			list_move_tail(&u->link, &not_cycle_list);
+			u->gc_maybe_cycle = 0;
+			scan_children(&u->sk, inc_inflight_move_tail, NULL);
+		}
+	}
+	list_del(&cursor);
+
+	/*
+	 * not_cycle_list contains those sockets which do not make up a
+	 * cycle.  Restore these to the inflight list.
+	 */
+	while (!list_empty(&not_cycle_list)) {
+		u = list_entry(not_cycle_list.next, struct bus_sock, link);
+		u->gc_candidate = 0;
+		list_move_tail(&u->link, &gc_inflight_list);
+	}
+
+	/*
+	 * Now gc_candidates contains only garbage.  Restore original
+	 * inflight counters for these as well, and remove the skbuffs
+	 * which are creating the cycle(s).
+	 */
+	skb_queue_head_init(&hitlist);
+	list_for_each_entry(u, &gc_candidates, link)
+	scan_children(&u->sk, inc_inflight, &hitlist);
+
+	spin_unlock(&bus_gc_lock);
+
+	/* Here we are. Hitlist is filled. Die. */
+	__skb_queue_purge(&hitlist);
+
+	spin_lock(&bus_gc_lock);
+
+	/* All candidates should have been detached by now. */
+	BUG_ON(!list_empty(&gc_candidates));
+	gc_in_progress = false;
+	wake_up(&bus_gc_wait);
+
+ out:
+	spin_unlock(&bus_gc_lock);
+}
-- 
1.7.10

^ permalink raw reply related

* [PATCH net-next 10/15] net: bus: Add the AF_BUS socket address family to KBuild
From: Vincent Sanders @ 2012-06-29 16:45 UTC (permalink / raw)
  To: netdev, linux-kernel, David S. Miller
  Cc: Javier Martinez Canillas, Vincent Sanders
In-Reply-To: <1340988354-26981-1-git-send-email-vincent.sanders@collabora.co.uk>

From: Javier Martinez Canillas <javier.martinez@collabora.co.uk>

This patch adds the AF_BUS code to the Linux Kernel build system.

Signed-off-by: Javier Martinez Canillas <javier.martinez@collabora.co.uk>
Signed-off-by: Vincent Sanders <vincent.sanders@collabora.co.uk>
---
 net/Kconfig      |    1 +
 net/Makefile     |    1 +
 net/bus/Kconfig  |   15 +++++++++++++++
 net/bus/Makefile |    7 +++++++
 4 files changed, 24 insertions(+)
 create mode 100644 net/bus/Kconfig
 create mode 100644 net/bus/Makefile

diff --git a/net/Kconfig b/net/Kconfig
index 245831b..339a630 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -47,6 +47,7 @@ menu "Networking options"
 
 source "net/packet/Kconfig"
 source "net/unix/Kconfig"
+source "net/bus/Kconfig"
 source "net/xfrm/Kconfig"
 source "net/iucv/Kconfig"
 
diff --git a/net/Makefile b/net/Makefile
index 4f4ee08..ad0e900 100644
--- a/net/Makefile
+++ b/net/Makefile
@@ -19,6 +19,7 @@ obj-$(CONFIG_NETFILTER)		+= netfilter/
 obj-$(CONFIG_INET)		+= ipv4/
 obj-$(CONFIG_XFRM)		+= xfrm/
 obj-$(CONFIG_UNIX)		+= unix/
+obj-$(CONFIG_AF_BUS)		+= bus/
 obj-$(CONFIG_NET)		+= ipv6/
 obj-$(CONFIG_PACKET)		+= packet/
 obj-$(CONFIG_NET_KEY)		+= key/
diff --git a/net/bus/Kconfig b/net/bus/Kconfig
new file mode 100644
index 0000000..5f01410
--- /dev/null
+++ b/net/bus/Kconfig
@@ -0,0 +1,15 @@
+#
+# Bus Domain Sockets
+#
+
+config AF_BUS
+	tristate "Bus domain sockets (EXPERIMENTAL)"
+	depends on EXPERIMENTAL
+	---help---
+	  If you say Y here, you will include support for Bus domain sockets.
+	  These sockets are used to create communication buses for IPC.
+
+	  To compile this driver as a module, choose M here: the module will be
+	  called bus.
+
+	  Say N unless you know what you are doing.
diff --git a/net/bus/Makefile b/net/bus/Makefile
new file mode 100644
index 0000000..8c1fea2
--- /dev/null
+++ b/net/bus/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for the Linux bus domain socket layer.
+#
+
+obj-$(CONFIG_AF_BUS)	+= af-bus.o
+
+af-bus-y		:= af_bus.o garbage.o
-- 
1.7.10

^ permalink raw reply related

* [PATCH net-next 11/15] netlink: connector: implement cn_netlink_reply
From: Vincent Sanders @ 2012-06-29 16:45 UTC (permalink / raw)
  To: netdev, linux-kernel, David S. Miller; +Cc: Alban Crequy
In-Reply-To: <1340988354-26981-1-git-send-email-vincent.sanders@collabora.co.uk>

From: Alban Crequy <alban.crequy@collabora.co.uk>

In a connector callback, it was not possible to reply to a message only to a
sender. This patch implements cn_netlink_reply(). It uses the connector socket
to send an unicast netlink message back to the sender.

The following pseudo-code can be used from a connector callback:

        struct cn_msg *cn_reply;
        cn_reply = kzalloc(sizeof(struct cn_msg)
                + sizeof(struct ..._nl_cfg_reply), GFP_KERNEL);

        cn_reply->id = msg->id;
        cn_reply->seq = msg->seq;
        cn_reply->ack = msg->ack  + 1;
        cn_reply->len = sizeof(struct ..._nl_cfg_reply);
        cn_reply->flags = 0;

        rr = cn_netlink_reply(cn_reply, nsp->pid, GFP_KERNEL);

Signed-off-by: Alban Crequy <alban.crequy@collabora.co.uk>
---
 drivers/connector/connector.c |   32 ++++++++++++++++++++++++++++++++
 include/linux/connector.h     |    1 +
 2 files changed, 33 insertions(+)

diff --git a/drivers/connector/connector.c b/drivers/connector/connector.c
index 34e0e9e..a728d33 100644
--- a/drivers/connector/connector.c
+++ b/drivers/connector/connector.c
@@ -118,6 +118,38 @@ int cn_netlink_send(struct cn_msg *msg, u32 __group, gfp_t gfp_mask)
 EXPORT_SYMBOL_GPL(cn_netlink_send);
 
 /*
+ * Send an unicast reply from a connector callback
+ *
+ */
+int cn_netlink_reply(struct cn_msg *msg, u32 pid, gfp_t gfp_mask)
+{
+	unsigned int size;
+	struct sk_buff *skb;
+	struct nlmsghdr *nlh;
+	struct cn_msg *data;
+	struct cn_dev *dev = &cdev;
+
+	size = NLMSG_SPACE(sizeof(*msg) + msg->len);
+
+	skb = alloc_skb(size, gfp_mask);
+	if (!skb)
+		return -ENOMEM;
+
+	nlh = nlmsg_put(skb, 0, msg->seq, NLMSG_DONE, size - sizeof(*nlh), 0);
+	if (nlh == NULL) {
+		kfree_skb(skb);
+		return -EMSGSIZE;
+	}
+
+	data = nlmsg_data(nlh);
+
+	memcpy(data, msg, sizeof(*data) + msg->len);
+
+	return netlink_unicast(dev->nls, skb, pid, 1);
+}
+EXPORT_SYMBOL_GPL(cn_netlink_reply);
+
+/*
  * Callback helper - queues work and setup destructor for given data.
  */
 static int cn_call_callback(struct sk_buff *skb)
diff --git a/include/linux/connector.h b/include/linux/connector.h
index 7638407..c27be60 100644
--- a/include/linux/connector.h
+++ b/include/linux/connector.h
@@ -125,6 +125,7 @@ int cn_add_callback(struct cb_id *id, const char *name,
 		    void (*callback)(struct cn_msg *, struct netlink_skb_parms *));
 void cn_del_callback(struct cb_id *);
 int cn_netlink_send(struct cn_msg *, u32, gfp_t);
+int cn_netlink_reply(struct cn_msg *, u32, gfp_t);
 
 int cn_queue_add_callback(struct cn_queue_dev *dev, const char *name,
 			  struct cb_id *id,
-- 
1.7.10

^ permalink raw reply related

* [PATCH net-next 12/15] netlink: connector: Add idx and val identifiers for netfilter D-Bus
From: Vincent Sanders @ 2012-06-29 16:45 UTC (permalink / raw)
  To: netdev, linux-kernel, David S. Miller
  Cc: Javier Martinez Canillas, Alban Crequy
In-Reply-To: <1340988354-26981-1-git-send-email-vincent.sanders@collabora.co.uk>

From: Javier Martinez Canillas <javier.martinez@collabora.co.uk>

The D-bus IPC system implements a transport that uses AF_BUS sockets to
send D-Bus messages to the peers. This allows decouple the routing logic
from the daemon and move it to the kernel which has the advantage of
reducing the number of context switches and the messages copied to
user-space.

A D-Bus protocol aware netfilter module decide which peer can recive a
given message based on a set of D-Bus match rules. These match rules
are set from user-space using the netlink connector API.

Signed-off-by: Javier Martinez Canillas <javier.martinez@collabora.co.uk>
Signed-off-by: Alban Crequy <alban.crequy@collabora.co.uk>
---
 include/linux/connector.h |    4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/include/linux/connector.h b/include/linux/connector.h
index c27be60..519d010 100644
--- a/include/linux/connector.h
+++ b/include/linux/connector.h
@@ -44,8 +44,10 @@
 #define CN_VAL_DRBD			0x1
 #define CN_KVP_IDX			0x9	/* HyperV KVP */
 #define CN_KVP_VAL			0x1	/* queries from the kernel */
+#define CN_IDX_NFDBUS                   0xA     /* netfilter D-Bus */
+#define CN_VAL_NFDBUS                   0x1
 
-#define CN_NETLINK_USERS		10	/* Highest index + 1 */
+#define CN_NETLINK_USERS		11	/* Highest index + 1 */
 
 /*
  * Maximum connector's message size.
-- 
1.7.10

^ permalink raw reply related

* [PATCH net-next 13/15] netfilter: nfdbus: Add D-bus message parsing
From: Vincent Sanders @ 2012-06-29 16:45 UTC (permalink / raw)
  To: netdev, linux-kernel, David S. Miller
  Cc: Javier Martinez Canillas, Alban Crequy
In-Reply-To: <1340988354-26981-1-git-send-email-vincent.sanders@collabora.co.uk>

From: Javier Martinez Canillas <javier.martinez@collabora.co.uk>

The netfilter D-Bus module needs to parse D-bus messages sent by
applications to decide whether a peer can receive or not a D-Bus
message. Add D-bus message parsing logic to be able to analyze.

Signed-off-by: Javier Martinez Canillas <javier.martinez@collabora.co.uk>
Signed-off-by: Alban Crequy <alban.crequy@collabora.co.uk>
---
 net/netfilter/nfdbus/message.c |  194 ++++++++++++++++++++++++++++++++++++++++
 net/netfilter/nfdbus/message.h |   71 +++++++++++++++
 2 files changed, 265 insertions(+)
 create mode 100644 net/netfilter/nfdbus/message.c
 create mode 100644 net/netfilter/nfdbus/message.h

diff --git a/net/netfilter/nfdbus/message.c b/net/netfilter/nfdbus/message.c
new file mode 100644
index 0000000..93c409c
--- /dev/null
+++ b/net/netfilter/nfdbus/message.c
@@ -0,0 +1,194 @@
+/*
+ * message.c  Basic D-Bus message parsing
+ *
+ * Copyright (C) 2010-2012  Collabora Ltd
+ * Authors:	Alban Crequy <alban.crequy@collabora.co.uk>
+ * Copyright (C) 2002, 2003, 2004, 2005  Red Hat Inc.
+ * Copyright (C) 2002, 2003  CodeFactory AB
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <linux/slab.h>
+
+#include "message.h"
+
+int dbus_message_type_from_string(const char *type_str)
+{
+	if (strcmp(type_str, "method_call") == 0)
+		return DBUS_MESSAGE_TYPE_METHOD_CALL;
+	if (strcmp(type_str, "method_return") == 0)
+		return DBUS_MESSAGE_TYPE_METHOD_RETURN;
+	else if (strcmp(type_str, "signal") == 0)
+		return DBUS_MESSAGE_TYPE_SIGNAL;
+	else if (strcmp(type_str, "error") == 0)
+		return DBUS_MESSAGE_TYPE_ERROR;
+	else
+		return DBUS_MESSAGE_TYPE_INVALID;
+}
+
+int dbus_message_parse(unsigned char *message, size_t len,
+		       struct dbus_message *dbus_message)
+{
+	unsigned char *cur;
+	int array_header_len;
+
+	dbus_message->message = message;
+
+	if (len < 4 + 4 + 4 + 4 || message[1] == 0 || message[1] > 4)
+		return -EINVAL;
+
+	dbus_message->type = message[1];
+	dbus_message->body_length = *((u32 *)(message + 4));
+	cur = message + 12;
+	array_header_len = *(u32 *)cur;
+	dbus_message->len_offset = 12;
+	cur += 4;
+	while (cur < message + len
+	       && cur < message + 12 + 4 + array_header_len) {
+		int header_code;
+		int signature_len;
+		unsigned char *signature;
+		int str_len;
+		unsigned char *str;
+
+		/* D-Bus alignment craziness */
+		if ((cur - message) % 8 != 0)
+			cur += 8 - (cur - message) % 8;
+
+		header_code = *(char *)cur;
+		cur++;
+		signature_len = *(char *)cur;
+		/* All header fields of the current D-Bus spec have a simple
+		 * type, either o, s, g, or u */
+		if (signature_len != 1)
+			return -EINVAL;
+		cur++;
+		signature = cur;
+		cur += signature_len + 1;
+		if (signature[0] != 'o' &&
+		    signature[0] != 's' &&
+		    signature[0] != 'g' &&
+		    signature[0] != 'u')
+			return -EINVAL;
+
+		if (signature[0] == 'u') {
+			cur += 4;
+			continue;
+		}
+
+		if (signature[0] != 'g') {
+			str_len = *(u32 *)cur;
+			cur += 4;
+		} else {
+			str_len = *(char *)cur;
+			cur += 1;
+		}
+
+		str = cur;
+		switch (header_code) {
+		case 1:
+			dbus_message->path = str;
+			break;
+		case 2:
+			dbus_message->interface = str;
+			break;
+		case 3:
+			dbus_message->member = str;
+			break;
+		case 6:
+			dbus_message->destination = str;
+			break;
+		case 7:
+			dbus_message->sender = str;
+			break;
+		case 8:
+			dbus_message->body_signature = str;
+			break;
+		}
+		cur += str_len + 1;
+	}
+
+	dbus_message->padding_end = (8 - (cur - message) % 8) % 8;
+
+	/* Jump to body D-Bus alignment craziness */
+	if ((cur - message) % 8 != 0)
+		cur += 8 - (cur - message) % 8;
+	dbus_message->new_header_offset = cur - message;
+
+	if (dbus_message->new_header_offset
+	    + dbus_message->body_length != len) {
+		pr_warn("Message truncated? " \
+			"Header %d + Body %d != Length %zd\n",
+			dbus_message->new_header_offset,
+			dbus_message->body_length, len);
+		return -EINVAL;
+	}
+
+	if (dbus_message->body_signature &&
+	    dbus_message->body_signature[0] == 's') {
+		int str_len;
+		str_len = *(u32 *)cur;
+		cur += 4;
+		dbus_message->arg0 = cur;
+		cur += str_len + 1;
+	}
+
+	if ((cur - message) % 4 != 0)
+		cur += 4 - (cur - message) % 4;
+
+	if (dbus_message->body_signature &&
+	    dbus_message->body_signature[0] == 's' &&
+	    dbus_message->body_signature[1] == 's') {
+		int str_len;
+		str_len = *(u32 *)cur;
+		cur += 4;
+		dbus_message->arg1 = cur;
+		cur += str_len + 1;
+	}
+
+	if ((cur - message) % 4 != 0)
+		cur += 4 - (cur - message) % 4;
+
+	if (dbus_message->body_signature &&
+	    dbus_message->body_signature[0] == 's' &&
+	    dbus_message->body_signature[1] == 's' &&
+	    dbus_message->body_signature[2] == 's') {
+		int str_len;
+		str_len = *(u32 *)cur;
+		cur += 4;
+		dbus_message->arg2 = cur;
+		cur += str_len + 1;
+	}
+
+	if ((cur - message) % 4 != 0)
+		cur += 4 - (cur - message) % 4;
+
+	if (dbus_message->type == DBUS_MESSAGE_TYPE_SIGNAL &&
+	    dbus_message->sender && dbus_message->path &&
+	    dbus_message->interface && dbus_message->member &&
+	    dbus_message->arg0 &&
+	    strcmp(dbus_message->sender, "org.freedesktop.DBus") == 0 &&
+	    strcmp(dbus_message->interface, "org.freedesktop.DBus") == 0 &&
+	    strcmp(dbus_message->path, "/org/freedesktop/DBus") == 0) {
+		if (strcmp(dbus_message->member, "NameAcquired") == 0)
+			dbus_message->name_acquired = dbus_message->arg0;
+		else if (strcmp(dbus_message->member, "NameLost") == 0)
+			dbus_message->name_lost = dbus_message->arg0;
+	}
+
+	return 0;
+}
diff --git a/net/netfilter/nfdbus/message.h b/net/netfilter/nfdbus/message.h
new file mode 100644
index 0000000..e3ea4d3
--- /dev/null
+++ b/net/netfilter/nfdbus/message.h
@@ -0,0 +1,71 @@
+/*
+ * message.h  Basic D-Bus message parsing
+ *
+ * Copyright (C) 2010  Collabora Ltd
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifndef DBUS_MESSAGE_H
+#define DBUS_MESSAGE_H
+
+#include <linux/list.h>
+
+#define DBUS_MAXIMUM_MATCH_RULE_LENGTH 1024
+
+/* Types of message */
+
+#define DBUS_MESSAGE_TYPE_INVALID       0
+#define DBUS_MESSAGE_TYPE_METHOD_CALL   1
+#define DBUS_MESSAGE_TYPE_METHOD_RETURN 2
+#define DBUS_MESSAGE_TYPE_ERROR         3
+#define DBUS_MESSAGE_TYPE_SIGNAL        4
+#define DBUS_NUM_MESSAGE_TYPES          5
+
+/* No need to implement a feature-complete parser. It only implement what is
+ * needed by the bus. */
+struct dbus_message {
+	char *message;
+	size_t len;
+	size_t new_len;
+
+	/* direct pointers to the fields */
+	int type;
+	char *path;
+	char *interface;
+	char *member;
+	char *destination;
+	char *sender;
+	char *body_signature;
+	int body_length;
+	char *arg0;
+	char *arg1;
+	char *arg2;
+	char *name_acquired;
+	char *name_lost;
+
+	/* How to add the 'sender' field in the headers */
+	int new_header_offset;
+	int len_offset;
+	int padding_end;
+};
+
+int dbus_message_type_from_string(const char *type_str);
+
+int dbus_message_parse(unsigned char *message, size_t len,
+		       struct dbus_message *dbus_message);
+
+#endif /* DBUS_MESSAGE_H */
-- 
1.7.10

^ permalink raw reply related

* [PATCH net-next 14/15] netfilter: nfdbus: Add D-bus match rule implementation
From: Vincent Sanders @ 2012-06-29 16:45 UTC (permalink / raw)
  To: netdev, linux-kernel, David S. Miller
  Cc: Javier Martinez Canillas, Alban Crequy
In-Reply-To: <1340988354-26981-1-git-send-email-vincent.sanders@collabora.co.uk>

From: Javier Martinez Canillas <javier.martinez@collabora.co.uk>

The D-Bus netfilter module needs to decode D-Bus match rules to decide
if a given peer can receive or not a D-Bus message. Add a match rule
implementation to be used by the netfilter D-Bus module.

Signed-off-by: Javier Martinez Canillas <javier.martinez@collabora.co.uk>
Signed-off-by: Alban Crequy <alban.crequy@collabora.co.uk>
---
 net/netfilter/nfdbus/matchrule.c | 1132 ++++++++++++++++++++++++++++++++++++++
 net/netfilter/nfdbus/matchrule.h |   82 +++
 2 files changed, 1214 insertions(+)
 create mode 100644 net/netfilter/nfdbus/matchrule.c
 create mode 100644 net/netfilter/nfdbus/matchrule.h

diff --git a/net/netfilter/nfdbus/matchrule.c b/net/netfilter/nfdbus/matchrule.c
new file mode 100644
index 0000000..4106bd5
--- /dev/null
+++ b/net/netfilter/nfdbus/matchrule.c
@@ -0,0 +1,1132 @@
+/*
+ * matchrule.c  D-Bus match rule implementation
+ *
+ * Based on signals.c from dbus
+ *
+ * Copyright (C) 2010  Collabora, Ltd.
+ * Copyright (C) 2003, 2005  Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include "matchrule.h"
+
+#include <linux/rbtree.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+
+#include "message.h"
+
+enum bus_match_flags {
+	BUS_MATCH_MESSAGE_TYPE            = 1 << 0,
+	BUS_MATCH_INTERFACE               = 1 << 1,
+	BUS_MATCH_MEMBER                  = 1 << 2,
+	BUS_MATCH_SENDER                  = 1 << 3,
+	BUS_MATCH_DESTINATION             = 1 << 4,
+	BUS_MATCH_PATH                    = 1 << 5,
+	BUS_MATCH_ARGS                    = 1 << 6,
+	BUS_MATCH_PATH_NAMESPACE          = 1 << 7,
+	BUS_MATCH_CLIENT_IS_EAVESDROPPING = 1 << 8
+};
+
+struct bus_match_rule {
+	/* For debugging only*/
+	char *rule_text;
+
+	unsigned int flags; /**< BusMatchFlags */
+
+	int   message_type;
+	char *interface;
+	char *member;
+	char *sender;
+	char *destination;
+	char *path;
+
+	unsigned int *arg_lens;
+	char **args;
+	int args_len;
+
+	/* bus_match_rule is attached to rule_pool, either in a simple
+	 * double-linked list if the rule does not have any interface, or in a
+	 * red-black tree sorted by interface. If several rules can have the
+	 * same interface, the first one is attached with struct rb_node and the
+	 * next ones are in the list
+	 */
+
+	struct rb_node node;
+	/* Doubly-linked non-circular list. If the rule has an interface, it is
+	 * in the rb tree and the single head is right here. Otherwise, the
+	 * single head is in rule_pool->rules_without_iface. With this data
+	 * structure, we don't need any allocation to insert or remove the rule.
+	 */
+	struct hlist_head first;
+	struct hlist_node list;
+
+	/* used to delete all names from the tree */
+	struct list_head del_list;
+};
+
+struct dbus_name {
+	struct rb_node node;
+	char *name;
+
+	/* used to delete all names from the tree */
+	struct list_head del_list;
+};
+
+#define BUS_MATCH_ARG_IS_PATH  0x8000000u
+
+#define DBUS_STRING_MAX_LENGTH 1024
+
+/** Max length of a match rule string; to keep people from hosing the
+ * daemon with some huge rule
+ */
+#define DBUS_MAXIMUM_MATCH_RULE_LENGTH 1024
+
+struct bus_match_rule *bus_match_rule_new(gfp_t gfp_flags)
+{
+	struct bus_match_rule *rule;
+
+	rule = kzalloc(sizeof(struct bus_match_rule), gfp_flags);
+	if (rule == NULL)
+		return NULL;
+
+	return rule;
+}
+
+void bus_match_rule_free(struct bus_match_rule *rule)
+{
+	kfree(rule->rule_text);
+	kfree(rule->interface);
+	kfree(rule->member);
+	kfree(rule->sender);
+	kfree(rule->destination);
+	kfree(rule->path);
+	kfree(rule->arg_lens);
+
+	/* can't use dbus_free_string_array() since there
+	 * are embedded NULL
+	 */
+	if (rule->args) {
+		int i;
+
+		i = 0;
+		while (i < rule->args_len) {
+			kfree(rule->args[i]);
+			++i;
+		}
+
+		kfree(rule->args);
+	}
+
+	kfree(rule);
+}
+
+static int
+bus_match_rule_set_message_type(struct bus_match_rule *rule,
+				int type,
+				gfp_t gfp_flags)
+{
+	rule->flags |= BUS_MATCH_MESSAGE_TYPE;
+
+	rule->message_type = type;
+
+	return 1;
+}
+
+static int
+bus_match_rule_set_interface(struct bus_match_rule *rule,
+			     const char *interface,
+			     gfp_t gfp_flags)
+{
+	char *new;
+
+	WARN_ON(!interface);
+
+	new = kstrdup(interface, gfp_flags);
+	if (new == NULL)
+		return 0;
+
+	rule->flags |= BUS_MATCH_INTERFACE;
+	kfree(rule->interface);
+	rule->interface = new;
+
+	return 1;
+}
+
+static int
+bus_match_rule_set_member(struct bus_match_rule *rule,
+			  const char *member,
+			  gfp_t gfp_flags)
+{
+	char *new;
+
+	WARN_ON(!member);
+
+	new = kstrdup(member, gfp_flags);
+	if (new == NULL)
+		return 0;
+
+	rule->flags |= BUS_MATCH_MEMBER;
+	kfree(rule->member);
+	rule->member = new;
+
+	return 1;
+}
+
+static int
+bus_match_rule_set_sender(struct bus_match_rule *rule,
+			  const char *sender,
+			  gfp_t gfp_flags)
+{
+	char *new;
+
+	WARN_ON(!sender);
+
+	new = kstrdup(sender, gfp_flags);
+	if (new == NULL)
+		return 0;
+
+	rule->flags |= BUS_MATCH_SENDER;
+	kfree(rule->sender);
+	rule->sender = new;
+
+	return 1;
+}
+
+static int
+bus_match_rule_set_destination(struct bus_match_rule *rule,
+			       const char   *destination,
+			       gfp_t gfp_flags)
+{
+	char *new;
+
+	WARN_ON(!destination);
+
+	new = kstrdup(destination, gfp_flags);
+	if (new == NULL)
+		return 0;
+
+	rule->flags |= BUS_MATCH_DESTINATION;
+	kfree(rule->destination);
+	rule->destination = new;
+
+	return 1;
+}
+
+#define ISWHITE(c) (((c) == ' ') || ((c) == '\t') || ((c) == '\n') || \
+		    ((c) == '\r'))
+
+static int find_key(const char *str, int start, char *key, int *value_pos)
+{
+	const char *p;
+	const char *s;
+	const char *key_start;
+	const char *key_end;
+
+	s = str;
+
+	p = s + start;
+
+	while (*p && ISWHITE(*p))
+		++p;
+
+	key_start = p;
+
+	while (*p && *p != '=' && !ISWHITE(*p))
+		++p;
+
+	key_end = p;
+
+	while (*p && ISWHITE(*p))
+		++p;
+
+	if (key_start == key_end) {
+		/* Empty match rules or trailing whitespace are OK */
+		*value_pos = p - s;
+		return 1;
+	}
+
+	if (*p != '=') {
+		pr_warn("Match rule has a key with no subsequent '=' character");
+		return 0;
+	}
+	++p;
+
+	strncat(key, key_start, key_end - key_start);
+
+	*value_pos = p - s;
+
+	return 1;
+}
+
+static int find_value(const char *str, int start, const char *key, char *value,
+		      int *value_end)
+{
+	const char *p;
+	const char *s;
+	char quote_char;
+	int orig_len;
+
+	orig_len = strlen(value);
+
+	s = str;
+
+	p = s + start;
+
+	quote_char = '\0';
+
+	while (*p) {
+		if (quote_char == '\0') {
+			switch (*p) {
+			case '\0':
+				goto done;
+
+			case '\'':
+				quote_char = '\'';
+				goto next;
+
+			case ',':
+				++p;
+				goto done;
+
+			case '\\':
+				quote_char = '\\';
+				goto next;
+
+			default:
+				strncat(value, p, 1);
+			}
+		} else if (quote_char == '\\') {
+			/*\ only counts as an escape if escaping a quote mark */
+			if (*p != '\'')
+				strncat(value, "\\", 1);
+
+			strncat(value, p, 1);
+
+			quote_char = '\0';
+		} else {
+			if (*p == '\'')
+				quote_char = '\0';
+			else
+				strncat(value, p, 1);
+		}
+
+next:
+		++p;
+	}
+
+done:
+
+	if (quote_char == '\\')
+		strncat(value, "\\", 1);
+	else if (quote_char == '\'') {
+		pr_warn("Unbalanced quotation marks in match rule");
+		return 0;
+	}
+
+	/* Zero-length values are allowed */
+
+	*value_end = p - s;
+
+	return 1;
+}
+
+/* duplicates aren't allowed so the real legitimate max is only 6 or
+ * so. Leaving extra so we don't have to bother to update it.
+ * FIXME this is sort of busted now with arg matching, but we let
+ * you match on up to 10 args for now
+ */
+#define MAX_RULE_TOKENS 16
+
+/* this is slightly too high level to be termed a "token"
+ * but let's not be pedantic.
+ */
+struct rule_token {
+	char *key;
+	char *value;
+};
+
+static int tokenize_rule(const char *rule_text,
+			 struct rule_token tokens[MAX_RULE_TOKENS],
+			 gfp_t gfp_flags)
+{
+	int i;
+	int pos;
+	int retval;
+
+	retval = 0;
+
+	i = 0;
+	pos = 0;
+	while (i < MAX_RULE_TOKENS &&
+	       pos < strlen(rule_text)) {
+		char *key;
+		char *value;
+
+		key = kzalloc(DBUS_STRING_MAX_LENGTH, gfp_flags);
+		if (!key) {
+			pr_err("Out of memory");
+			return 0;
+		}
+
+		value = kzalloc(DBUS_STRING_MAX_LENGTH, gfp_flags);
+		if (!value) {
+			kfree(key);
+			pr_err("Out of memory");
+			return 0;
+		}
+
+		if (!find_key(rule_text, pos, key, &pos))
+			goto out;
+
+		if (strlen(key) == 0)
+			goto next;
+
+		tokens[i].key = key;
+
+		if (!find_value(rule_text, pos, tokens[i].key, value, &pos))
+			goto out;
+
+		tokens[i].value = value;
+
+next:
+		++i;
+	}
+
+	retval = 1;
+
+out:
+	if (!retval) {
+		i = 0;
+		while (tokens[i].key || tokens[i].value) {
+			kfree(tokens[i].key);
+			kfree(tokens[i].value);
+			tokens[i].key = NULL;
+			tokens[i].value = NULL;
+			++i;
+		}
+	}
+
+	return retval;
+}
+
+/*
+ * The format is comma-separated with strings quoted with single quotes
+ * as for the shell (to escape a literal single quote, use '\'').
+ *
+ * type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',
+ * member='Foo', path='/bar/foo',destination=':452345.34'
+ *
+ */
+struct bus_match_rule *bus_match_rule_parse(const char *rule_text,
+					    gfp_t gfp_flags)
+{
+	struct bus_match_rule *rule;
+	struct rule_token tokens[MAX_RULE_TOKENS+1]; /* NULL termination + 1 */
+	int i;
+
+	if (strlen(rule_text) > DBUS_MAXIMUM_MATCH_RULE_LENGTH) {
+		pr_warn("Match rule text is %ld bytes, maximum is %d",
+			    strlen(rule_text),
+			    DBUS_MAXIMUM_MATCH_RULE_LENGTH);
+		return NULL;
+	}
+
+	memset(tokens, '\0', sizeof(tokens));
+
+	rule = bus_match_rule_new(gfp_flags);
+	if (rule == NULL) {
+		pr_err("Out of memory");
+		goto failed;
+	}
+
+	rule->rule_text = kstrdup(rule_text, gfp_flags);
+	if (rule->rule_text == NULL) {
+		pr_err("Out of memory");
+		goto failed;
+	}
+
+	if (!tokenize_rule(rule_text, tokens, gfp_flags))
+		goto failed;
+
+	i = 0;
+	while (tokens[i].key != NULL) {
+		const char *key = tokens[i].key;
+		const char *value = tokens[i].value;
+
+		if (strcmp(key, "type") == 0) {
+			int t;
+
+			if (rule->flags & BUS_MATCH_MESSAGE_TYPE) {
+				pr_warn("Key %s specified twice in match rule\n",
+					key);
+				goto failed;
+			}
+
+			t = dbus_message_type_from_string(value);
+
+			if (t == DBUS_MESSAGE_TYPE_INVALID) {
+				pr_warn("Invalid message type (%s) in match rule\n",
+					value);
+				goto failed;
+			}
+
+			if (!bus_match_rule_set_message_type(rule, t,
+							     gfp_flags)) {
+				pr_err("Out of memeory");
+				goto failed;
+			}
+		} else if (strcmp(key, "sender") == 0) {
+			if (rule->flags & BUS_MATCH_SENDER) {
+				pr_warn("Key %s specified twice in match rule\n",
+					key);
+				goto failed;
+			}
+
+			if (!bus_match_rule_set_sender(rule, value,
+						       gfp_flags)) {
+				pr_err("Out of memeory");
+				goto failed;
+			}
+		} else if (strcmp(key, "interface") == 0) {
+			if (rule->flags & BUS_MATCH_INTERFACE) {
+				pr_warn("Key %s specified twice in match rule\n",
+					key);
+				goto failed;
+			}
+
+			if (!bus_match_rule_set_interface(rule, value,
+							  gfp_flags)) {
+				pr_err("Out of memeory");
+				goto failed;
+			}
+		} else if (strcmp(key, "member") == 0) {
+			if (rule->flags & BUS_MATCH_MEMBER) {
+				pr_warn("Key %s specified twice in match rule\n",
+					key);
+				goto failed;
+			}
+
+			if (!bus_match_rule_set_member(rule, value,
+						       gfp_flags)) {
+				pr_err("Out of memeory");
+				goto failed;
+			}
+		} else if (strcmp(key, "destination") == 0) {
+			if (rule->flags & BUS_MATCH_DESTINATION) {
+				pr_warn("Key %s specified twice in match rule\n",
+					key);
+				goto failed;
+			}
+
+			if (!bus_match_rule_set_destination(rule, value,
+							    gfp_flags)) {
+				pr_err("Out of memeory");
+				goto failed;
+			}
+		} else if (strcmp(key, "eavesdrop") == 0) {
+			if (strcmp(value, "true") == 0) {
+				rule->flags |= BUS_MATCH_CLIENT_IS_EAVESDROPPING;
+			} else if (strcmp(value, "false") == 0) {
+				rule->flags &= ~(BUS_MATCH_CLIENT_IS_EAVESDROPPING);
+			} else {
+				pr_warn("eavesdrop='%s' is invalid, " \
+					"it should be 'true' or 'false'\n",
+					value);
+				goto failed;
+			}
+		} else if (strncmp(key, "arg", 3) != 0) {
+			pr_warn("Unknown key \"%s\" in match rule\n",
+				   key);
+			goto failed;
+		}
+
+		++i;
+	}
+
+	goto out;
+
+failed:
+	if (rule) {
+		bus_match_rule_free(rule);
+		rule = NULL;
+	}
+
+out:
+
+	i = 0;
+	while (tokens[i].key || tokens[i].value) {
+		WARN_ON(i >= MAX_RULE_TOKENS);
+		kfree(tokens[i].key);
+		kfree(tokens[i].value);
+		++i;
+	}
+
+	return rule;
+}
+
+/* return the match rule containing the hlist_head. It may not be the first
+ * match rule in the list. */
+struct bus_match_rule *match_rule_search(struct rb_root *root,
+					 const char *interface)
+{
+	struct rb_node *node = root->rb_node;
+
+	while (node) {
+		struct bus_match_rule *data =
+			container_of(node, struct bus_match_rule, node);
+		int result;
+
+		result = strcmp(interface, data->interface);
+
+		if (result < 0)
+			node = node->rb_left;
+		else if (result > 0)
+			node = node->rb_right;
+		else
+			return data;
+	}
+	return NULL;
+}
+
+void match_rule_insert(struct rb_root *root, struct bus_match_rule *data)
+{
+	struct rb_node **new = &(root->rb_node), *parent = NULL;
+
+	/* Figure out where to put new node */
+	while (*new) {
+		struct bus_match_rule *this =
+			container_of(*new, struct bus_match_rule, node);
+		int result = strcmp(data->interface, this->interface);
+
+		parent = *new;
+		if (result < 0)
+			new = &((*new)->rb_left);
+		else if (result > 0)
+			new = &((*new)->rb_right);
+		else {
+			/* the head is not used */
+			INIT_HLIST_HEAD(&data->first);
+			/* Add it at the beginning of the list */
+			hlist_add_head(&data->list, &this->first);
+			return;
+		}
+	}
+
+	/* this rule is single in its list */
+	INIT_HLIST_HEAD(&data->first);
+	hlist_add_head(&data->list, &data->first);
+
+	/* Add new node and rebalance tree. */
+	rb_link_node(&data->node, parent, new);
+	rb_insert_color(&data->node, root);
+}
+
+struct bus_match_maker *bus_matchmaker_new(gfp_t gfp_flags)
+{
+	struct bus_match_maker *matchmaker;
+	int i;
+
+	matchmaker = kzalloc(sizeof(struct bus_match_maker), gfp_flags);
+	if (matchmaker == NULL)
+		return NULL;
+
+	for (i = DBUS_MESSAGE_TYPE_INVALID; i < DBUS_NUM_MESSAGE_TYPES; i++) {
+		struct rule_pool *p = matchmaker->rules_by_type + i;
+
+		p->rules_by_iface = RB_ROOT;
+	}
+
+	kref_init(&matchmaker->kref);
+
+	return matchmaker;
+}
+
+void bus_matchmaker_free(struct kref *kref)
+{
+	struct bus_match_maker *matchmaker;
+	struct list_head del_list;
+	struct rb_node *n;
+	int i;
+
+	matchmaker = container_of(kref, struct bus_match_maker, kref);
+
+	/* free names */
+	INIT_LIST_HEAD(&del_list);
+	n = matchmaker->names.rb_node;
+	if (n) {
+		struct dbus_name *dbus_name, *cur, *tmp;
+
+		dbus_name = rb_entry(n, struct dbus_name, node);
+		list_add_tail(&dbus_name->del_list, &del_list);
+
+		list_for_each_entry(cur, &del_list, del_list) {
+			struct dbus_name *right, *left;
+			if (cur->node.rb_right) {
+				right = rb_entry(cur->node.rb_right,
+						 struct dbus_name, node);
+				list_add_tail(&right->del_list, &del_list);
+			}
+			if (cur->node.rb_left) {
+				left = rb_entry(cur->node.rb_left,
+						struct dbus_name, node);
+				list_add_tail(&left->del_list, &del_list);
+			}
+		}
+		list_for_each_entry_safe(dbus_name, tmp, &del_list, del_list) {
+			kfree(dbus_name->name);
+			list_del(&dbus_name->del_list);
+			kfree(dbus_name);
+		}
+	}
+	WARN_ON(!list_empty_careful(&del_list));
+
+	/* free match rules */
+	for (i = 0 ; i < DBUS_NUM_MESSAGE_TYPES ; i++) {
+		struct rule_pool *pool = matchmaker->rules_by_type + i;
+		struct bus_match_rule *match_rule, *cur, *tmp;
+		struct hlist_node *list_tmp, *list_tmp2;
+
+		/* free match rules from the list */
+		hlist_for_each_entry_safe(cur, list_tmp, list_tmp2,
+					  &pool->rules_without_iface, list) {
+			bus_match_rule_free(cur);
+		}
+
+		/* free match rules from the tree */
+		if (!pool->rules_by_iface.rb_node)
+			continue;
+		match_rule = rb_entry(pool->rules_by_iface.rb_node,
+				      struct bus_match_rule, node);
+		list_add_tail(&match_rule->del_list, &del_list);
+
+		list_for_each_entry(cur, &del_list, del_list) {
+			struct bus_match_rule *right, *left;
+			if (cur->node.rb_right) {
+				right = rb_entry(cur->node.rb_right,
+						 struct bus_match_rule, node);
+				list_add_tail(&right->del_list, &del_list);
+			}
+			if (cur->node.rb_left) {
+				left = rb_entry(cur->node.rb_left,
+						struct bus_match_rule, node);
+				list_add_tail(&left->del_list, &del_list);
+			}
+		}
+		list_for_each_entry_safe(match_rule, tmp, &del_list, del_list) {
+			/* keep a ref during the loop to ensure the first
+			 * iteration of the loop does not delete it */
+			hlist_for_each_entry_safe(cur, list_tmp, list_tmp2,
+						  &match_rule->first, list) {
+				if (cur != match_rule)
+					bus_match_rule_free(cur);
+			}
+			list_del(&match_rule->del_list);
+			bus_match_rule_free(match_rule);
+		}
+		WARN_ON(!list_empty_careful(&del_list));
+	}
+
+	kfree(matchmaker);
+}
+
+/* The rule can't be modified after it's added. */
+int bus_matchmaker_add_rule(struct bus_match_maker *matchmaker,
+			    struct bus_match_rule *rule)
+{
+	struct rule_pool *pool;
+
+	WARN_ON(rule->message_type < 0);
+	WARN_ON(rule->message_type >= DBUS_NUM_MESSAGE_TYPES);
+
+	pool = matchmaker->rules_by_type + rule->message_type;
+
+	if (rule->interface)
+		match_rule_insert(&pool->rules_by_iface, rule);
+	else
+		hlist_add_head(&rule->list, &pool->rules_without_iface);
+
+	return 1;
+}
+
+static int match_rule_equal(struct bus_match_rule *a,
+			    struct bus_match_rule *b)
+{
+	if (a->flags != b->flags)
+		return 0;
+
+	if ((a->flags & BUS_MATCH_MESSAGE_TYPE) &&
+	    a->message_type != b->message_type)
+		return 0;
+
+	if ((a->flags & BUS_MATCH_MEMBER) &&
+	    strcmp(a->member, b->member) != 0)
+		return 0;
+
+	if ((a->flags & BUS_MATCH_PATH) &&
+	    strcmp(a->path, b->path) != 0)
+		return 0;
+
+	if ((a->flags & BUS_MATCH_INTERFACE) &&
+	    strcmp(a->interface, b->interface) != 0)
+		return 0;
+
+	if ((a->flags & BUS_MATCH_SENDER) &&
+	    strcmp(a->sender, b->sender) != 0)
+		return 0;
+
+	if ((a->flags & BUS_MATCH_DESTINATION) &&
+	    strcmp(a->destination, b->destination) != 0)
+		return 0;
+
+	if (a->flags & BUS_MATCH_ARGS) {
+		int i;
+
+		if (a->args_len != b->args_len)
+			return 0;
+
+		i = 0;
+		while (i < a->args_len) {
+			int length;
+
+			if ((a->args[i] != NULL) != (b->args[i] != NULL))
+				return 0;
+
+			if (a->arg_lens[i] != b->arg_lens[i])
+				return 0;
+
+			length = a->arg_lens[i] & ~BUS_MATCH_ARG_IS_PATH;
+
+			if (a->args[i] != NULL) {
+				WARN_ON(!b->args[i]);
+				if (memcmp(a->args[i], b->args[i], length) != 0)
+					return 0;
+			}
+
+			++i;
+		}
+	}
+
+	return 1;
+}
+
+/* Remove a single rule which is equal to the given rule by value */
+void bus_matchmaker_remove_rule_by_value(struct bus_match_maker *matchmaker,
+					 struct bus_match_rule *rule)
+{
+	struct rule_pool *pool;
+
+	WARN_ON(rule->message_type < 0);
+	WARN_ON(rule->message_type >= DBUS_NUM_MESSAGE_TYPES);
+
+	pool = matchmaker->rules_by_type + rule->message_type;
+
+	if (rule->interface) {
+		struct bus_match_rule *head =
+			match_rule_search(&pool->rules_by_iface,
+					  rule->interface);
+
+		struct hlist_node *cur;
+		struct bus_match_rule *cur_rule;
+		hlist_for_each_entry(cur_rule, cur, &head->first, list) {
+			if (match_rule_equal(cur_rule, rule)) {
+				hlist_del(cur);
+				if (hlist_empty(&head->first))
+					rb_erase(&head->node,
+						 &pool->rules_by_iface);
+				bus_match_rule_free(cur_rule);
+				break;
+			}
+		}
+	} else {
+		struct hlist_head *head = &pool->rules_without_iface;
+
+		struct hlist_node *cur;
+		struct bus_match_rule *cur_rule;
+		hlist_for_each_entry(cur_rule, cur, head, list) {
+			if (match_rule_equal(cur_rule, rule)) {
+				hlist_del(cur);
+				bus_match_rule_free(cur_rule);
+				break;
+			}
+		}
+	}
+
+}
+
+static int connection_is_primary_owner(struct bus_match_maker *connection,
+				       const char *service_name)
+{
+	struct rb_node *node = connection->names.rb_node;
+
+	if (!service_name)
+		return 0;
+
+	while (node) {
+		struct dbus_name *data = container_of(node, struct dbus_name,
+						      node);
+		int result;
+
+		result = strcmp(service_name, data->name);
+
+		if (result < 0)
+			node = node->rb_left;
+		else if (result > 0)
+			node = node->rb_right;
+		else
+			return 1;
+	}
+	return 0;
+}
+
+static int match_rule_matches(struct bus_match_maker *matchmaker,
+			      struct bus_match_maker *sender,
+			      int eavesdrop,
+			      struct bus_match_rule *rule,
+			      const struct dbus_message *message)
+{
+	/* Don't consider the rule if this is a eavesdropping match rule
+	 * and eavesdropping is not allowed on that peer */
+	if ((rule->flags & BUS_MATCH_CLIENT_IS_EAVESDROPPING) && !eavesdrop)
+		return 0;
+
+	/* Since D-Bus 1.5.6, match rules do not match messages which have a
+	 * DESTINATION field unless the match rule specifically requests this
+	 * by specifying eavesdrop='true' in the match rule. */
+	if (message->destination &&
+	    !(rule->flags & BUS_MATCH_CLIENT_IS_EAVESDROPPING))
+		return 0;
+
+	if (rule->flags & BUS_MATCH_MEMBER) {
+		const char *member;
+
+		WARN_ON(!rule->member);
+
+		member = message->member;
+		if (member == NULL)
+			return 0;
+
+		if (strcmp(member, rule->member) != 0)
+			return 0;
+	}
+
+	if (rule->flags & BUS_MATCH_SENDER) {
+		WARN_ON(!rule->sender);
+
+		if (sender == NULL) {
+			if (strcmp(rule->sender,
+				   "org.freedesktop.DBus") != 0)
+				return 0;
+		} else
+			if (!connection_is_primary_owner(sender, rule->sender))
+				return 0;
+	}
+
+	if (rule->flags & BUS_MATCH_DESTINATION) {
+		const char *destination;
+
+		WARN_ON(!rule->destination);
+
+		destination = message->destination;
+		if (destination == NULL)
+			return 0;
+
+		/* This will not just work out of the box because it this is
+		 * an eavesdropping match rule. */
+		if (matchmaker == NULL) {
+			if (strcmp(rule->destination,
+				   "org.freedesktop.DBus") != 0)
+				return 0;
+		} else
+			if (!connection_is_primary_owner(matchmaker,
+							 rule->destination))
+				return 0;
+	}
+
+	if (rule->flags & BUS_MATCH_PATH) {
+		const char *path;
+
+		WARN_ON(!rule->path);
+
+		path = message->path;
+		if (path == NULL)
+			return 0;
+
+		if (strcmp(path, rule->path) != 0)
+			return 0;
+	}
+
+	return 1;
+}
+
+static bool get_recipients_from_list(struct bus_match_maker *matchmaker,
+				     struct bus_match_maker *sender,
+				     int eavesdrop,
+				     struct hlist_head *rules,
+				     const struct dbus_message *message)
+{
+	struct hlist_node *cur;
+	struct bus_match_rule *rule;
+
+	if (rules == NULL) {
+		pr_debug("no rules of this type\n");
+		return 0;
+	}
+
+	hlist_for_each_entry(rule, cur, rules, list) {
+		if (match_rule_matches(matchmaker, sender, eavesdrop, rule,
+					message)) {
+			pr_debug("[YES] deliver with match rule \"%s\"\n",
+				 rule->rule_text);
+			return 1;
+		} else {
+			pr_debug("[NO]  deliver with match rule \"%s\"\n",
+				 rule->rule_text);
+		}
+	}
+	pr_debug("[NO]  no match rules\n");
+	return 0;
+}
+
+static struct hlist_head
+*bus_matchmaker_get_rules(struct bus_match_maker *matchmaker,
+			  int message_type, const char *interface)
+{
+	static struct hlist_head empty = {0,};
+	struct rule_pool *p;
+
+	WARN_ON(message_type < 0);
+	WARN_ON(message_type >= DBUS_NUM_MESSAGE_TYPES);
+
+	p = matchmaker->rules_by_type + message_type;
+
+	if (interface == NULL)
+		return &p->rules_without_iface;
+	else {
+		struct bus_match_rule *rule =
+			match_rule_search(&p->rules_by_iface, interface);
+		if (rule)
+			return &rule->first;
+		else
+			return &empty;
+	}
+}
+
+bool bus_matchmaker_filter(struct bus_match_maker *matchmaker,
+			   struct bus_match_maker *sender,
+			   int eavesdrop,
+			   const struct dbus_message *message)
+{
+	int type;
+	const char *interface;
+	struct hlist_head *neither, *just_type, *just_iface, *both;
+
+	type = message->type;
+	interface = message->interface;
+
+	neither = bus_matchmaker_get_rules(matchmaker,
+					   DBUS_MESSAGE_TYPE_INVALID, NULL);
+	just_type = just_iface = both = NULL;
+
+	if (interface != NULL)
+		just_iface = bus_matchmaker_get_rules(matchmaker,
+						      DBUS_MESSAGE_TYPE_INVALID,
+						      interface);
+
+	if (type > DBUS_MESSAGE_TYPE_INVALID && type < DBUS_NUM_MESSAGE_TYPES) {
+		just_type = bus_matchmaker_get_rules(matchmaker, type, NULL);
+
+		if (interface != NULL)
+			both = bus_matchmaker_get_rules(matchmaker, type,
+							interface);
+	}
+
+	if (get_recipients_from_list(matchmaker, sender, eavesdrop, neither,
+				     message))
+		return 1;
+	if (get_recipients_from_list(matchmaker, sender, eavesdrop, just_iface,
+				     message))
+		return 1;
+	if (get_recipients_from_list(matchmaker, sender, eavesdrop, just_type,
+				     message))
+		return 1;
+	if (get_recipients_from_list(matchmaker, sender, eavesdrop, both,
+				     message))
+		return 1;
+
+	return connection_is_primary_owner(matchmaker, message->destination);
+}
+
+void bus_matchmaker_add_name(struct bus_match_maker *matchmaker,
+			     const char *name,
+			     gfp_t gfp_flags)
+{
+	struct dbus_name *dbus_name;
+	struct rb_node **new = &(matchmaker->names.rb_node), *parent = NULL;
+
+	dbus_name = kmalloc(sizeof(struct dbus_name), gfp_flags);
+	if (!dbus_name)
+		return;
+	dbus_name->name = kstrdup(name, gfp_flags);
+	if (!dbus_name->name)
+		return;
+
+	/* Figure out where to put new node */
+	while (*new) {
+		struct dbus_name *this = container_of(*new, struct dbus_name,
+						      node);
+		int result = strcmp(dbus_name->name, this->name);
+
+		parent = *new;
+		if (result < 0)
+			new = &((*new)->rb_left);
+		else if (result > 0)
+			new = &((*new)->rb_right);
+		else
+			return;
+	}
+
+	/* Add new node and rebalance tree. */
+	rb_link_node(&dbus_name->node, parent, new);
+	rb_insert_color(&dbus_name->node, &matchmaker->names);
+}
+
+void bus_matchmaker_remove_name(struct bus_match_maker *matchmaker,
+				const char *name)
+{
+	struct rb_node *node = matchmaker->names.rb_node;
+
+	while (node) {
+		struct dbus_name *data = container_of(node, struct dbus_name,
+						      node);
+		int result;
+
+		result = strcmp(name, data->name);
+
+		if (result < 0)
+			node = node->rb_left;
+		else if (result > 0)
+			node = node->rb_right;
+		else {
+			rb_erase(&data->node, &matchmaker->names);
+			kfree(data->name);
+			kfree(data);
+		}
+	}
+
+}
+
diff --git a/net/netfilter/nfdbus/matchrule.h b/net/netfilter/nfdbus/matchrule.h
new file mode 100644
index 0000000..e16580c
--- /dev/null
+++ b/net/netfilter/nfdbus/matchrule.h
@@ -0,0 +1,82 @@
+/*
+ * signals.h  Bus signal connection implementation
+ *
+ * Copyright (C) 2003  Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifndef BUS_SIGNALS_H
+#define BUS_SIGNALS_H
+
+#include <linux/gfp.h>
+#include <linux/list.h>
+#include <linux/rbtree.h>
+#include <linux/slab.h>
+#include <net/af_bus.h>
+
+#include "message.h"
+
+struct bus_match_rule *bus_match_rule_new(gfp_t gfp_flags);
+void bus_match_rule_free(struct bus_match_rule *rule);
+
+struct bus_match_rule *bus_match_rule_parse(const char *rule_text,
+					    gfp_t gfp_flags);
+
+struct rule_pool {
+	/* Maps non-NULL interface names to a list of bus_match_rule */
+	struct rb_root rules_by_iface;
+
+	/* List of bus_match_rule which don't specify an interface */
+	struct hlist_head rules_without_iface;
+};
+
+struct bus_match_maker {
+	struct sockaddr_bus addr;
+
+	struct hlist_node table_node;
+
+	/* Pools of rules, grouped by the type of message they match. 0
+	 * (DBUS_MESSAGE_TYPE_INVALID) represents rules that do not specify a
+	 * message type.
+	 */
+	struct rule_pool rules_by_type[DBUS_NUM_MESSAGE_TYPES];
+
+	struct rb_root names;
+
+	struct kref kref;
+};
+
+
+struct bus_match_maker *bus_matchmaker_new(gfp_t gfp_flags);
+void bus_matchmaker_free(struct kref *kref);
+
+int bus_matchmaker_add_rule(struct bus_match_maker *matchmaker,
+			    struct bus_match_rule *rule);
+void bus_matchmaker_remove_rule_by_value(struct bus_match_maker *matchmaker,
+					 struct bus_match_rule *value);
+
+bool bus_matchmaker_filter(struct bus_match_maker *matchmaker,
+			   struct bus_match_maker *sender,
+			   int eavesdrop,
+			   const struct dbus_message *message);
+
+void bus_matchmaker_add_name(struct bus_match_maker *matchmaker,
+			     const char *name, gfp_t gfp_flags);
+void bus_matchmaker_remove_name(struct bus_match_maker *matchmaker,
+				const char *name);
+
+#endif /* BUS_SIGNALS_H */
-- 
1.7.10

^ permalink raw reply related

* [PATCH net-next 15/15] netfilter: add netfilter D-Bus module
From: Vincent Sanders @ 2012-06-29 16:45 UTC (permalink / raw)
  To: netdev, linux-kernel, David S. Miller; +Cc: Alban Crequy
In-Reply-To: <1340988354-26981-1-git-send-email-vincent.sanders@collabora.co.uk>

From: Alban Crequy <alban.crequy@collabora.co.uk>

AF_BUS has netfilter hooks on the packet sending path. This allows the
netfilter subsystem to register netfilter hook handlers.

The netfilter_dbus module allows to inspect D-Bus messages and take
actions based on the information contained on these messages.

Signed-off-by: Alban Crequy <alban.crequy@collabora.co.uk>
---
 net/netfilter/Kconfig         |    2 +
 net/netfilter/Makefile        |    3 +
 net/netfilter/nfdbus/Kconfig  |   12 ++
 net/netfilter/nfdbus/Makefile |    6 +
 net/netfilter/nfdbus/nfdbus.c |  456 +++++++++++++++++++++++++++++++++++++++++
 net/netfilter/nfdbus/nfdbus.h |   44 ++++
 6 files changed, 523 insertions(+)
 create mode 100644 net/netfilter/nfdbus/Kconfig
 create mode 100644 net/netfilter/nfdbus/Makefile
 create mode 100644 net/netfilter/nfdbus/nfdbus.c
 create mode 100644 net/netfilter/nfdbus/nfdbus.h

diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig
index c19b214..a105d9b 100644
--- a/net/netfilter/Kconfig
+++ b/net/netfilter/Kconfig
@@ -1187,3 +1187,5 @@ endmenu
 source "net/netfilter/ipset/Kconfig"
 
 source "net/netfilter/ipvs/Kconfig"
+
+source "net/netfilter/nfdbus/Kconfig"
diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile
index 1c5160f..6dd4ade 100644
--- a/net/netfilter/Makefile
+++ b/net/netfilter/Makefile
@@ -123,3 +123,6 @@ obj-$(CONFIG_IP_SET) += ipset/
 
 # IPVS
 obj-$(CONFIG_IP_VS) += ipvs/
+
+# Dbus
+obj-$(CONFIG_NETFILTER_DBUS) += nfdbus/
diff --git a/net/netfilter/nfdbus/Kconfig b/net/netfilter/nfdbus/Kconfig
new file mode 100644
index 0000000..25699a1
--- /dev/null
+++ b/net/netfilter/nfdbus/Kconfig
@@ -0,0 +1,12 @@
+#
+# Netfilter D-Bus module configuration
+#
+config NETFILTER_DBUS
+	tristate "Netfilter D-bus (EXPERIMENTAL)"
+	depends on AF_BUS && CONNECTOR && EXPERIMENTAL
+	---help---
+	  If you say Y here, you will include support for a netfilter hook to
+	  parse D-Bus messages sent using the AF_BUS socket address family.
+
+	  To compile this as a module, choose M here: the module will be
+	  called netfilter_dbus.
diff --git a/net/netfilter/nfdbus/Makefile b/net/netfilter/nfdbus/Makefile
new file mode 100644
index 0000000..1a825f8
--- /dev/null
+++ b/net/netfilter/nfdbus/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for the netfilter D-Bus module
+#
+obj-$(CONFIG_NETFILTER_DBUS) += netfilter_dbus.o
+
+netfilter_dbus-y := nfdbus.o message.o matchrule.o
diff --git a/net/netfilter/nfdbus/nfdbus.c b/net/netfilter/nfdbus/nfdbus.c
new file mode 100644
index 0000000..f6642e2
--- /dev/null
+++ b/net/netfilter/nfdbus/nfdbus.c
@@ -0,0 +1,456 @@
+/*
+ *  nfdbus.c - Netfilter module for AF_BUS/BUS_PROTO_DBUS.
+ */
+
+#define DRIVER_AUTHOR "Alban Crequy"
+#define DRIVER_DESC   "Netfilter module for AF_BUS/BUS_PROTO_DBUS."
+
+#include "nfdbus.h"
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/skbuff.h>
+#include <linux/netfilter.h>
+#include <linux/connector.h>
+#include <net/af_bus.h>
+
+#include "message.h"
+#include "matchrule.h"
+
+static struct nf_hook_ops nfho_dbus;
+
+static struct cb_id cn_cmd_id = { CN_IDX_NFDBUS, CN_VAL_NFDBUS };
+
+static unsigned int hash;
+
+/* Scoped by AF_BUS address */
+struct hlist_head matchrules_table[BUS_HASH_SIZE];
+DEFINE_SPINLOCK(matchrules_lock);
+
+static struct bus_match_maker *find_match_maker(struct sockaddr_bus *addr,
+		bool create, bool delete)
+{
+	u64 hash;
+	struct hlist_node *node;
+	struct bus_match_maker *matchmaker;
+	int path_len = strlen(addr->sbus_path);
+
+	hash = csum_partial(addr->sbus_path,
+			    strlen(addr->sbus_path), 0);
+	hash ^= addr->sbus_addr.s_addr;
+	hash ^= hash >> 32;
+	hash ^= hash >> 16;
+	hash ^= hash >> 8;
+	hash &= 0xff;
+
+	spin_lock(&matchrules_lock);
+	hlist_for_each_entry(matchmaker, node, &matchrules_table[hash],
+			     table_node) {
+		if (addr->sbus_family == matchmaker->addr.sbus_family &&
+		    addr->sbus_addr.s_addr == matchmaker->addr.sbus_addr.s_addr &&
+		    !memcmp(addr->sbus_path, matchmaker->addr.sbus_path,
+			   path_len)) {
+			kref_get(&matchmaker->kref);
+			if (delete)
+				hlist_del(&matchmaker->table_node);
+			spin_unlock(&matchrules_lock);
+			pr_debug("Found matchmaker for hash %llu", hash);
+			return matchmaker;
+		}
+	}
+	spin_unlock(&matchrules_lock);
+
+	if (!create) {
+		pr_debug("Matchmaker for hash %llu not found", hash);
+		return NULL;
+	}
+
+	matchmaker = bus_matchmaker_new(GFP_ATOMIC);
+	matchmaker->addr.sbus_family = addr->sbus_family;
+	matchmaker->addr.sbus_addr.s_addr = addr->sbus_addr.s_addr;
+	memcpy(matchmaker->addr.sbus_path, addr->sbus_path, BUS_PATH_MAX);
+
+	pr_debug("Create new matchmaker for hash %llu\n", hash);
+	spin_lock(&matchrules_lock);
+	hlist_add_head(&matchmaker->table_node, &matchrules_table[hash]);
+	kref_get(&matchmaker->kref);
+	spin_unlock(&matchrules_lock);
+	return matchmaker;
+}
+
+static unsigned int dbus_filter(unsigned int hooknum,
+				struct sk_buff *skb,
+				const struct net_device *in,
+				const struct net_device *out,
+				int (*okfn)(struct sk_buff *))
+{
+	struct bus_send_context	*sendctx;
+	struct bus_match_maker *matchmaker = NULL;
+	struct bus_match_maker *sender = NULL;
+	struct dbus_message msg = {0,};
+	unsigned char *data;
+	size_t len;
+	int err;
+	int ret;
+
+	if (!skb->sk || skb->sk->sk_family != PF_BUS) {
+		WARN(1, "netfilter_dbus received an invalid skb");
+		return NF_DROP;
+	}
+
+	data = skb->data;
+	sendctx = BUSCB(skb).sendctx;
+	if (!sendctx || !sendctx->sender || !sendctx->sender_socket) {
+		WARN(1, "netfilter_dbus received an AF_BUS packet" \
+		     " without context. This is a bug. Dropping the"
+			" packet.");
+		return NF_DROP;
+	}
+
+	if (sendctx->sender_socket->sk->sk_protocol != BUS_PROTO_DBUS) {
+		/* This kernel module is for D-Bus. It must not
+		 * interfere with other users of AF_BUS. */
+		return NF_ACCEPT;
+	}
+	if (sendctx->recipient)
+		matchmaker = find_match_maker(sendctx->recipient, false, false);
+
+	len =  skb_tail_pointer(skb) - data;
+
+	if (sendctx->to_master && sendctx->main_recipient) {
+		pr_debug("AF_BUS packet to the bus master. ACCEPT.\n");
+		ret = NF_ACCEPT;
+		goto out;
+	}
+
+	if (sendctx->main_recipient && !sendctx->bus_master_side) {
+		pr_debug("AF_BUS packet from a peer to a peer (unicast). ACCEPT.\n");
+		ret = NF_ACCEPT;
+		goto out;
+	}
+
+	err = dbus_message_parse(data, len, &msg);
+	if (err) {
+		if (!sendctx->main_recipient) {
+			pr_debug("AF_BUS packet for an eavesdropper or " \
+				 "multicast is not parsable. DROP.\n");
+			ret = NF_DROP;
+			goto out;
+		} else if (sendctx->bus_master_side) {
+			pr_debug("AF_BUS packet from bus master is not parsable. ACCEPT.\n");
+			ret = NF_ACCEPT;
+			goto out;
+		} else {
+			pr_debug("AF_BUS packet from peer is not parsable. DROP.\n");
+			ret = NF_DROP;
+			goto out;
+		}
+	}
+
+	if (sendctx->bus_master_side && !sendctx->main_recipient) {
+		pr_debug("AF_BUS packet '%s' from the bus master is for an " \
+			 "eavesdropper. DROP.\n",
+		       msg.member ? msg.member : "");
+		ret = NF_DROP;
+		goto out;
+	}
+	if (sendctx->bus_master_side) {
+		if (msg.name_acquired) {
+			pr_debug("New name: %s [%p %p].\n",
+				 msg.name_acquired, sendctx->sender,
+				 sendctx->recipient);
+
+			sender = find_match_maker(sendctx->sender, true, false);
+			bus_matchmaker_add_name(sender, msg.name_acquired,
+						GFP_ATOMIC);
+		}
+		if (msg.name_lost) {
+			pr_debug("Lost name: %s [%p %p].\n",
+				 msg.name_lost, sendctx->sender,
+				 sendctx->recipient);
+
+			sender = find_match_maker(sendctx->sender, true, false);
+			bus_matchmaker_remove_name(sender, msg.name_acquired);
+		}
+
+		pr_debug("AF_BUS packet '%s' from the bus master. ACCEPT.\n",
+			 msg.member ? msg.member : "");
+		ret = NF_ACCEPT;
+		goto out;
+	}
+
+	pr_debug("Multicast AF_BUS packet, %ld bytes, " \
+		 "considering recipient %lld...\n", len,
+		 sendctx->recipient ? sendctx->recipient->sbus_addr.s_addr : 0);
+
+	pr_debug("Message type %d %s->%s [iface: %s][member: %s][matchmaker=%p]...\n",
+		 msg.type,
+		 msg.sender ? msg.sender : "",
+		 msg.destination ? msg.destination : "",
+		 msg.interface ? msg.interface : "",
+		 msg.member ? msg.member : "",
+		 matchmaker);
+
+	if (!matchmaker) {
+		pr_debug("No match rules for this recipient. DROP.\n");
+		ret = NF_DROP;
+		goto out;
+	}
+
+	sender = find_match_maker(sendctx->sender, true, false);
+	err = bus_matchmaker_filter(matchmaker, sender, sendctx->eavesdropper,
+				    &msg);
+	if (err) {
+		pr_debug("Matchmaker: ACCEPT.\n");
+		ret = NF_ACCEPT;
+		goto out;
+	} else {
+		pr_debug("Matchmaker: DROP.\n");
+		ret = NF_DROP;
+		goto out;
+	}
+
+out:
+	if (matchmaker)
+		kref_put(&matchmaker->kref, bus_matchmaker_free);
+	if (sender)
+		kref_put(&sender->kref, bus_matchmaker_free);
+	return ret;
+}
+
+/* Taken from drbd_nl_send_reply() */
+static void nfdbus_nl_send_reply(struct cn_msg *msg, int ret_code)
+{
+	char buffer[sizeof(struct cn_msg)+sizeof(struct nfdbus_nl_cfg_reply)];
+	struct cn_msg *cn_reply = (struct cn_msg *) buffer;
+	struct nfdbus_nl_cfg_reply *reply =
+		(struct nfdbus_nl_cfg_reply *)cn_reply->data;
+	int rr;
+
+	memset(buffer, 0, sizeof(buffer));
+	cn_reply->id = msg->id;
+
+	cn_reply->seq = msg->seq;
+	cn_reply->ack = msg->ack  + 1;
+	cn_reply->len = sizeof(struct nfdbus_nl_cfg_reply);
+	cn_reply->flags = 0;
+
+	reply->ret_code = ret_code;
+
+	rr = cn_netlink_send(cn_reply, 0, GFP_NOIO);
+	if (rr && rr != -ESRCH)
+		pr_debug("nfdbus: cn_netlink_send()=%d\n", rr);
+}
+
+/**
+ * nfdbus_check_perm - check if a pid is allowed to update match rules
+ * @sockaddr_bus: the socket address of the bus
+ * @pid: the process id that wants to update the match rules set
+ *
+ * Test if a given process id is allowed to update the match rules set
+ * for this bus. Only the process that owns the bus master listen socket
+ * is allowed to update the match rules set for the bus.
+ */
+static bool nfdbus_check_perm(struct sockaddr_bus *sbusname, pid_t pid)
+{
+	struct net *net = get_net_ns_by_pid(pid);
+	struct sock *s;
+	struct bus_address *addr;
+	struct hlist_node *node;
+	int offset = (sbusname->sbus_path[0] == '\0');
+	int path_len = strnlen(sbusname->sbus_path + offset, BUS_PATH_MAX);
+	int len;
+	if (!net)
+		return false;
+
+	len = path_len + 1 + sizeof(__kernel_sa_family_t) +
+	      sizeof(struct bus_addr);
+
+	spin_lock(&bus_address_lock);
+
+	hlist_for_each_entry(addr, node, &bus_address_table[hash],
+			     table_node) {
+		s = addr->sock;
+
+		if (s->sk_protocol != BUS_PROTO_DBUS)
+			continue;
+
+		if (!net_eq(sock_net(s), net))
+			continue;
+
+		if (addr->len == len &&
+		    addr->name->sbus_family == sbusname->sbus_family &&
+		    addr->name->sbus_addr.s_addr == BUS_MASTER_ADDR &&
+		    bus_same_bus(addr->name, sbusname) &&
+		    pid_nr(s->sk_peer_pid) == pid) {
+			spin_unlock(&bus_address_lock);
+			return true;
+		}
+	}
+
+	spin_unlock(&bus_address_lock);
+
+	return false;
+}
+
+static void cn_cmd_cb(struct cn_msg *msg, struct netlink_skb_parms *nsp)
+{
+	struct nfdbus_nl_cfg_req *nlp = (struct nfdbus_nl_cfg_req *)msg->data;
+	struct cn_msg *cn_reply;
+	struct nfdbus_nl_cfg_reply *reply;
+	int retcode, rr;
+	pid_t pid = task_tgid_vnr(current);
+	int reply_size = sizeof(struct cn_msg)
+		+ sizeof(struct nfdbus_nl_cfg_reply);
+
+	pr_debug("nfdbus: %s nsp->pid=%d pid=%d\n", __func__, nsp->pid, pid);
+
+	if (!nfdbus_check_perm(&nlp->addr, pid)) {
+		pr_debug(KERN_ERR "nfdbus: pid=%d is not allowed!\n", pid);
+		retcode = EPERM;
+		goto fail;
+	}
+
+	cn_reply = kzalloc(reply_size, GFP_KERNEL);
+	if (!cn_reply) {
+		retcode = ENOMEM;
+		goto fail;
+	}
+	reply = (struct nfdbus_nl_cfg_reply *) cn_reply->data;
+
+	if (msg->len < sizeof(struct nfdbus_nl_cfg_req)) {
+		reply->ret_code = EINVAL;
+	} else if (nlp->cmd == NFDBUS_CMD_ADDMATCH) {
+		struct bus_match_rule *rule;
+		struct bus_match_maker *matchmaker;
+		reply->ret_code = 0;
+
+		if (msg->len == 0)
+			reply->ret_code = EINVAL;
+
+		rule = bus_match_rule_parse(nlp->data, GFP_ATOMIC);
+		if (rule) {
+			matchmaker = find_match_maker(&nlp->addr, true, false);
+			pr_debug("Add match rule for matchmaker %p\n",
+				 matchmaker);
+			bus_matchmaker_add_rule(matchmaker, rule);
+			kref_put(&matchmaker->kref, bus_matchmaker_free);
+		} else {
+			reply->ret_code = EINVAL;
+		}
+	} else if (nlp->cmd == NFDBUS_CMD_REMOVEMATCH) {
+		struct bus_match_rule *rule;
+		struct bus_match_maker *matchmaker;
+
+		rule = bus_match_rule_parse(nlp->data, GFP_ATOMIC);
+		matchmaker = find_match_maker(&nlp->addr, false, false);
+		if (!matchmaker) {
+			reply->ret_code = EINVAL;
+		} else {
+			pr_debug("Remove match rule for matchmaker %p\n",
+				 matchmaker);
+			bus_matchmaker_remove_rule_by_value(matchmaker, rule);
+			kref_put(&matchmaker->kref, bus_matchmaker_free);
+			reply->ret_code = 0;
+		}
+		bus_match_rule_free(rule);
+
+	} else if (nlp->cmd == NFDBUS_CMD_REMOVEALLMATCH) {
+		struct bus_match_maker *matchmaker;
+
+		matchmaker = find_match_maker(&nlp->addr, false, true);
+		if (!matchmaker) {
+			reply->ret_code = EINVAL;
+		} else {
+			pr_debug("Remove matchmaker %p\n", matchmaker);
+			kref_put(&matchmaker->kref, bus_matchmaker_free);
+			kref_put(&matchmaker->kref, bus_matchmaker_free);
+			reply->ret_code = 0;
+		}
+
+	} else {
+		reply->ret_code = EINVAL;
+	}
+
+	cn_reply->id = msg->id;
+	cn_reply->seq = msg->seq;
+	cn_reply->ack = msg->ack  + 1;
+	cn_reply->len = sizeof(struct nfdbus_nl_cfg_reply);
+	cn_reply->flags = 0;
+
+	rr = cn_netlink_reply(cn_reply, nsp->pid, GFP_KERNEL);
+	if (rr && rr != -ESRCH)
+		pr_debug("nfdbus: cn_netlink_send()=%d\n", rr);
+	pr_debug("nfdbus: cn_netlink_reply(pid=%d)=%d\n", nsp->pid, rr);
+
+	kfree(cn_reply);
+	return;
+fail:
+	nfdbus_nl_send_reply(msg, retcode);
+}
+
+static int __init nfdbus_init(void)
+{
+	int err;
+	struct bus_addr master_addr;
+
+	master_addr.s_addr = BUS_MASTER_ADDR;
+	hash = bus_compute_hash(master_addr);
+
+	pr_debug("Loading netfilter_dbus\n");
+
+	/* Install D-Bus netfilter hook */
+	nfho_dbus.hook     = dbus_filter;
+	nfho_dbus.hooknum  = NF_BUS_SENDING;
+	nfho_dbus.pf       = NFPROTO_BUS; /* Do not use PF_BUS, you fool! */
+	nfho_dbus.priority = 0;
+	nfho_dbus.owner = THIS_MODULE;
+	err = nf_register_hook(&nfho_dbus);
+	if (err)
+		return err;
+	pr_debug("Netfilter hook for D-Bus: installed.\n");
+
+	/* Install connector hook */
+	err = cn_add_callback(&cn_cmd_id, "nfdbus", cn_cmd_cb);
+	if (err)
+		goto err_cn_cmd_out;
+	pr_debug("Connector hook: installed.\n");
+
+	return 0;
+
+err_cn_cmd_out:
+	nf_unregister_hook(&nfho_dbus);
+
+	return err;
+}
+
+static void __exit nfdbus_cleanup(void)
+{
+	int i;
+	struct hlist_node *node, *tmp;
+	struct bus_match_maker *matchmaker;
+	nf_unregister_hook(&nfho_dbus);
+
+	cn_del_callback(&cn_cmd_id);
+
+	spin_lock(&matchrules_lock);
+	for (i = 0; i < BUS_HASH_SIZE; i++) {
+		hlist_for_each_entry_safe(matchmaker, node, tmp,
+					  &matchrules_table[i], table_node) {
+			hlist_del(&matchmaker->table_node);
+			kref_put(&matchmaker->kref, bus_matchmaker_free);
+		}
+	}
+	spin_unlock(&matchrules_lock);
+
+	pr_debug("Unloading netfilter_dbus\n");
+}
+
+module_init(nfdbus_init);
+module_exit(nfdbus_cleanup);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_ALIAS_NET_PF_PROTO(PF_BUS, BUS_PROTO_DBUS);
diff --git a/net/netfilter/nfdbus/nfdbus.h b/net/netfilter/nfdbus/nfdbus.h
new file mode 100644
index 0000000..477bde3
--- /dev/null
+++ b/net/netfilter/nfdbus/nfdbus.h
@@ -0,0 +1,44 @@
+/*
+ * nfdbus.h  Netfilter module for AF_BUS/BUS_PROTO_DBUS.
+ *
+ * Copyright (C) 2012  Collabora Ltd
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifndef NETFILTER_DBUS_H
+#define NETFILTER_DBUS_H
+
+#include <linux/types.h>
+#include <linux/bus.h>
+
+#define NFDBUS_CMD_ADDMATCH        0x01
+#define NFDBUS_CMD_REMOVEMATCH     0x02
+#define NFDBUS_CMD_REMOVEALLMATCH  0x03
+
+struct nfdbus_nl_cfg_req {
+	__u32 cmd;
+	__u32 len;
+	struct sockaddr_bus addr;
+	__u64 pad;
+	unsigned char data[0];
+};
+
+struct nfdbus_nl_cfg_reply {
+	__u32 ret_code;
+};
+
+#endif /* NETFILTER_DBUS_H */
-- 
1.7.10

^ permalink raw reply related

* Re: [PATCH net-next 13/15] netfilter: nfdbus: Add D-bus message parsing
From: Pablo Neira Ayuso @ 2012-06-29 17:11 UTC (permalink / raw)
  To: Vincent Sanders
  Cc: netdev, linux-kernel, David S. Miller, Javier Martinez Canillas,
	Alban Crequy
In-Reply-To: <1340988354-26981-14-git-send-email-vincent.sanders@collabora.co.uk>

On Fri, Jun 29, 2012 at 05:45:52PM +0100, Vincent Sanders wrote:
> From: Javier Martinez Canillas <javier.martinez@collabora.co.uk>
> 
> The netfilter D-Bus module needs to parse D-bus messages sent by
> applications to decide whether a peer can receive or not a D-Bus
> message. Add D-bus message parsing logic to be able to analyze.

Not talking about the entire patchset, only about the part I'm
responsible for.

I don't see why you think this belong to netfilter at all.

This doesn't integrate into the existing filtering infrastructure,
neither it extends it in any way.

> Signed-off-by: Javier Martinez Canillas <javier.martinez@collabora.co.uk>
> Signed-off-by: Alban Crequy <alban.crequy@collabora.co.uk>
> ---
>  net/netfilter/nfdbus/message.c |  194 ++++++++++++++++++++++++++++++++++++++++
>  net/netfilter/nfdbus/message.h |   71 +++++++++++++++
>  2 files changed, 265 insertions(+)
>  create mode 100644 net/netfilter/nfdbus/message.c
>  create mode 100644 net/netfilter/nfdbus/message.h
> 
> diff --git a/net/netfilter/nfdbus/message.c b/net/netfilter/nfdbus/message.c
> new file mode 100644
> index 0000000..93c409c
> --- /dev/null
> +++ b/net/netfilter/nfdbus/message.c
> @@ -0,0 +1,194 @@
> +/*
> + * message.c  Basic D-Bus message parsing
> + *
> + * Copyright (C) 2010-2012  Collabora Ltd
> + * Authors:	Alban Crequy <alban.crequy@collabora.co.uk>
> + * Copyright (C) 2002, 2003, 2004, 2005  Red Hat Inc.
> + * Copyright (C) 2002, 2003  CodeFactory AB
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
> + *
> + */
> +
> +#include <linux/slab.h>
> +
> +#include "message.h"
> +
> +int dbus_message_type_from_string(const char *type_str)
> +{
> +	if (strcmp(type_str, "method_call") == 0)
> +		return DBUS_MESSAGE_TYPE_METHOD_CALL;
> +	if (strcmp(type_str, "method_return") == 0)
> +		return DBUS_MESSAGE_TYPE_METHOD_RETURN;
> +	else if (strcmp(type_str, "signal") == 0)
> +		return DBUS_MESSAGE_TYPE_SIGNAL;
> +	else if (strcmp(type_str, "error") == 0)
> +		return DBUS_MESSAGE_TYPE_ERROR;
> +	else
> +		return DBUS_MESSAGE_TYPE_INVALID;
> +}
> +
> +int dbus_message_parse(unsigned char *message, size_t len,
> +		       struct dbus_message *dbus_message)
> +{
> +	unsigned char *cur;
> +	int array_header_len;
> +
> +	dbus_message->message = message;
> +
> +	if (len < 4 + 4 + 4 + 4 || message[1] == 0 || message[1] > 4)
> +		return -EINVAL;
> +
> +	dbus_message->type = message[1];
> +	dbus_message->body_length = *((u32 *)(message + 4));
> +	cur = message + 12;
> +	array_header_len = *(u32 *)cur;
> +	dbus_message->len_offset = 12;
> +	cur += 4;
> +	while (cur < message + len
> +	       && cur < message + 12 + 4 + array_header_len) {
> +		int header_code;
> +		int signature_len;
> +		unsigned char *signature;
> +		int str_len;
> +		unsigned char *str;
> +
> +		/* D-Bus alignment craziness */
> +		if ((cur - message) % 8 != 0)
> +			cur += 8 - (cur - message) % 8;
> +
> +		header_code = *(char *)cur;
> +		cur++;
> +		signature_len = *(char *)cur;
> +		/* All header fields of the current D-Bus spec have a simple
> +		 * type, either o, s, g, or u */
> +		if (signature_len != 1)
> +			return -EINVAL;
> +		cur++;
> +		signature = cur;
> +		cur += signature_len + 1;
> +		if (signature[0] != 'o' &&
> +		    signature[0] != 's' &&
> +		    signature[0] != 'g' &&
> +		    signature[0] != 'u')
> +			return -EINVAL;
> +
> +		if (signature[0] == 'u') {
> +			cur += 4;
> +			continue;
> +		}
> +
> +		if (signature[0] != 'g') {
> +			str_len = *(u32 *)cur;
> +			cur += 4;
> +		} else {
> +			str_len = *(char *)cur;
> +			cur += 1;
> +		}
> +
> +		str = cur;
> +		switch (header_code) {
> +		case 1:
> +			dbus_message->path = str;
> +			break;
> +		case 2:
> +			dbus_message->interface = str;
> +			break;
> +		case 3:
> +			dbus_message->member = str;
> +			break;
> +		case 6:
> +			dbus_message->destination = str;
> +			break;
> +		case 7:
> +			dbus_message->sender = str;
> +			break;
> +		case 8:
> +			dbus_message->body_signature = str;
> +			break;
> +		}
> +		cur += str_len + 1;
> +	}
> +
> +	dbus_message->padding_end = (8 - (cur - message) % 8) % 8;
> +
> +	/* Jump to body D-Bus alignment craziness */
> +	if ((cur - message) % 8 != 0)
> +		cur += 8 - (cur - message) % 8;
> +	dbus_message->new_header_offset = cur - message;
> +
> +	if (dbus_message->new_header_offset
> +	    + dbus_message->body_length != len) {
> +		pr_warn("Message truncated? " \
> +			"Header %d + Body %d != Length %zd\n",
> +			dbus_message->new_header_offset,
> +			dbus_message->body_length, len);
> +		return -EINVAL;
> +	}
> +
> +	if (dbus_message->body_signature &&
> +	    dbus_message->body_signature[0] == 's') {
> +		int str_len;
> +		str_len = *(u32 *)cur;
> +		cur += 4;
> +		dbus_message->arg0 = cur;
> +		cur += str_len + 1;
> +	}
> +
> +	if ((cur - message) % 4 != 0)
> +		cur += 4 - (cur - message) % 4;
> +
> +	if (dbus_message->body_signature &&
> +	    dbus_message->body_signature[0] == 's' &&
> +	    dbus_message->body_signature[1] == 's') {
> +		int str_len;
> +		str_len = *(u32 *)cur;
> +		cur += 4;
> +		dbus_message->arg1 = cur;
> +		cur += str_len + 1;
> +	}
> +
> +	if ((cur - message) % 4 != 0)
> +		cur += 4 - (cur - message) % 4;
> +
> +	if (dbus_message->body_signature &&
> +	    dbus_message->body_signature[0] == 's' &&
> +	    dbus_message->body_signature[1] == 's' &&
> +	    dbus_message->body_signature[2] == 's') {
> +		int str_len;
> +		str_len = *(u32 *)cur;
> +		cur += 4;
> +		dbus_message->arg2 = cur;
> +		cur += str_len + 1;
> +	}
> +
> +	if ((cur - message) % 4 != 0)
> +		cur += 4 - (cur - message) % 4;
> +
> +	if (dbus_message->type == DBUS_MESSAGE_TYPE_SIGNAL &&
> +	    dbus_message->sender && dbus_message->path &&
> +	    dbus_message->interface && dbus_message->member &&
> +	    dbus_message->arg0 &&
> +	    strcmp(dbus_message->sender, "org.freedesktop.DBus") == 0 &&
> +	    strcmp(dbus_message->interface, "org.freedesktop.DBus") == 0 &&
> +	    strcmp(dbus_message->path, "/org/freedesktop/DBus") == 0) {
> +		if (strcmp(dbus_message->member, "NameAcquired") == 0)
> +			dbus_message->name_acquired = dbus_message->arg0;
> +		else if (strcmp(dbus_message->member, "NameLost") == 0)
> +			dbus_message->name_lost = dbus_message->arg0;
> +	}
> +
> +	return 0;
> +}
> diff --git a/net/netfilter/nfdbus/message.h b/net/netfilter/nfdbus/message.h
> new file mode 100644
> index 0000000..e3ea4d3
> --- /dev/null
> +++ b/net/netfilter/nfdbus/message.h
> @@ -0,0 +1,71 @@
> +/*
> + * message.h  Basic D-Bus message parsing
> + *
> + * Copyright (C) 2010  Collabora Ltd
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
> + *
> + */
> +
> +#ifndef DBUS_MESSAGE_H
> +#define DBUS_MESSAGE_H
> +
> +#include <linux/list.h>
> +
> +#define DBUS_MAXIMUM_MATCH_RULE_LENGTH 1024
> +
> +/* Types of message */
> +
> +#define DBUS_MESSAGE_TYPE_INVALID       0
> +#define DBUS_MESSAGE_TYPE_METHOD_CALL   1
> +#define DBUS_MESSAGE_TYPE_METHOD_RETURN 2
> +#define DBUS_MESSAGE_TYPE_ERROR         3
> +#define DBUS_MESSAGE_TYPE_SIGNAL        4
> +#define DBUS_NUM_MESSAGE_TYPES          5
> +
> +/* No need to implement a feature-complete parser. It only implement what is
> + * needed by the bus. */
> +struct dbus_message {
> +	char *message;
> +	size_t len;
> +	size_t new_len;
> +
> +	/* direct pointers to the fields */
> +	int type;
> +	char *path;
> +	char *interface;
> +	char *member;
> +	char *destination;
> +	char *sender;
> +	char *body_signature;
> +	int body_length;
> +	char *arg0;
> +	char *arg1;
> +	char *arg2;
> +	char *name_acquired;
> +	char *name_lost;
> +
> +	/* How to add the 'sender' field in the headers */
> +	int new_header_offset;
> +	int len_offset;
> +	int padding_end;
> +};
> +
> +int dbus_message_type_from_string(const char *type_str);
> +
> +int dbus_message_parse(unsigned char *message, size_t len,
> +		       struct dbus_message *dbus_message);
> +
> +#endif /* DBUS_MESSAGE_H */
> -- 
> 1.7.10
> 
> --
> To unsubscribe from this list: send the line "unsubscribe netdev" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: [RFC] [TCP 1/3] tcp: Add MSG_NEW_PACKET flag to indicate preferable packet boundaries
From: Eric Dumazet @ 2012-06-29 17:15 UTC (permalink / raw)
  To: Andreas Gruenbacher; +Cc: netdev, linux-kernel, Herbert Xu, David S. Miller
In-Reply-To: <1340984335.25450.24.camel@gurkel.linbit>

On Fri, 2012-06-29 at 17:38 +0200, Andreas Gruenbacher wrote:

> The primary use case is fast Gigabit (10 or more) Ethernet connections
> with jumbo frames and switches that support them.  There, frames will go
> through unchanged and you can zero-copy receive all the time.
> 
> Not sure how well the approach scales to other kinds of connections; it
> may work often enough to be worth it.  When things get distorted between
> the sender and the receiver and tcp_recvbio() fails, the data can still
> be copied out of the socket as before.

If you have a packet loss, receiver can and will coalesce frames.

^ permalink raw reply

* Re: [net-next.git 4/4 (v9)] phy: add the EEE support and the way to access to the MMD registers.
From: Ben Hutchings @ 2012-06-29 17:36 UTC (permalink / raw)
  To: Giuseppe CAVALLARO; +Cc: netdev, eric.dumazet, rayagond, davem, yuvalmin
In-Reply-To: <1340867678-18375-5-git-send-email-peppe.cavallaro@st.com>

On Thu, 2012-06-28 at 09:14 +0200, Giuseppe CAVALLARO wrote:
> This patch adds the support for the Energy-Efficient Ethernet (EEE)
> to the Physical Abstraction Layer.
> To support the EEE we have to access to the MMD registers 3.20 and
> 7.60/61. So two new functions have been added to read/write the MMD
> registers (clause 45).
> 
> An Ethernet driver (I tested the stmmac) can invoke the phy_init_eee to properly
> check if the EEE is supported by the PHYs and it can also set the clock
> stop enable bit in the 3.0 register.
> The phy_get_eee_err can be used for reporting the number of time where
> the PHY failed to complete its normal wake sequence.
> 
> In the end, this patch also adds the EEE ethtool support implementing:
>  o phy_ethtool_set_eee
>  o phy_ethtool_get_eee
> 
> v1: initial patch
> v2: fixed some errors especially on naming convention
> v3: renamed again the mmd read/write functions thank to Ben's feedback
> v4: moved file to phy.c and added the ethtool support.
> v5: fixed phy_adv_to_eee, phy_eee_to_supported, phy_eee_to_adv return
>     values according to ethtool API (thanks to Ben's feedback).
>     Renamed some macros to avoid too long names.
> v6: fixed kernel-doc comments to be properly parsed.
>     Fixed the phy_init_eee function: we need to check which link mode
>     was autonegotiated and then the corresponding bits in 7.60 and 7.61
>     registers.
> v7: reviewed the way to get the negotiated settings.
> v8: fixed a problem in the phy_init_eee return value erroneously added
>     when included the phy_read_status call.
> v9: do not remove the MDIO_AN_EEE_ADV_100TX and MDIO_AN_EEE_ADV_1000T
>     and fixed the eee_{cap,lp,adv} declaration as "int" instead of u16.
> 
> Signed-off-by: Giuseppe Cavallaro <peppe.cavallaro@st.com>
[...]
Reviewed-by: Ben Hutchings <bhutchings@solarflare.com>

(but not tested in any way)

Ben.

-- 
Ben Hutchings, Staff Engineer, Solarflare
Not speaking for my employer; that's the marketing department's job.
They asked us to note that Solarflare product names are trademarked.

^ permalink raw reply

* tcp mtu probing oddity - small packets
From: George B. @ 2012-06-29 17:43 UTC (permalink / raw)
  To: netdev

This is mainly meant to put this in the archive in case anyone else
has this problem.  It was a hair-puller.

Setup is a load balancer doing source NAT to a linux server.  Server
had /proc/sys/net/ipv4/tcp_mtu_probing=2

Apparently the PMTU probing somehow gets confused and the flow gets
clamped to the tcp_base_mss value (512 byte packets).  But this PMTU
value is apparently cached for all traffic to the IP address and since
that is a source NAT, ALL flows from that IP address get clamped to
512 byte packets.  So if something goes wonky with one flow, it
impacts all flows because they all appear to come from the same IP
address.

Setting tcp_mtu_probing to 1 has cleared the issue for now but it may
come back from time to time if a flow gets into trouble and whatever
Linux is doing in the PMTU probing causes the flow to be clamped to
tcp_base_mss but assuming that PMTU will age out at some point after
the problem client goes away, things should return to normal behavior.

There's something about how the PMTU probes work  that apparently
cause them to fail when activated in this particular configuration and
clamp the packet size to tcp_base_mss for all flows to that IP
address. If you are behind a source NAT, all flows share the same IP
address and so all flows get clamped.

Linux 2.6.38-11

^ permalink raw reply

* Reporting a Kernel Bug
From: Lucas Willian Bocchi @ 2012-06-29 17:49 UTC (permalink / raw)
  To: netdev

Dear Sir

Proceeding as oriented, I'm sending the link to these probable bug.


https://bugzilla.kernel.org/show_bug.cgi?id=43901

Thanks for advance.

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox