[PATCH] libertas: Per-socket mesh-ttl via setsockopt().
Javier Cardona
javier at cozybit.com
Mon Jul 9 16:12:33 EDT 2007
Support for using setsockpt() to change the mesh-ttl on a network flow, i.e.
<snip>
sock = socket (PF_INET, SOCK_STREAM, 0);
setsockopt(sock, SOL_IP, MESH_SO_SET_TTL, &ttl, optlen);
ttl = 0;
getsockopt(sock, SOL_IP, MESH_SO_GET_TTL, &ttl, &optlen);
</snip>
Signed-off-by: Javier Cardona <javier at cozybit.com>
---
drivers/net/wireless/libertas/mesh_opts.c | 174 +++++++++++++++++++++++++++++
drivers/net/wireless/libertas/mesh_opts.h | 5 +
2 files changed, 179 insertions(+), 0 deletions(-)
diff --git a/drivers/net/wireless/libertas/mesh_opts.c b/drivers/net/wireless/libertas/mesh_opts.c
new file mode 100644
index 0000000..118eaed
--- /dev/null
+++ b/drivers/net/wireless/libertas/mesh_opts.c
@@ -0,0 +1,174 @@
+/*
+ * mesh_opts
+ *
+ * Author: Javier Cardona <javier at cozybit.com>
+ * Copyright: Marvell Semiconductors Inc., 2007
+ *
+ * Apply mesh-layer specific configuration to network flows. Currently this
+ * only supports the mesh TTL parameter.
+ *
+ * Users call setsockopt on sockets to configure mesh parameters. This module
+ * maintains a list of sockets (mesh_sks) that have different mesh parameters
+ * than the per-interface defaults. The driver will modify the mesh
+ * configuration for each outgoing frame that belongs to one of the sockets in
+ * the mesh_sks list.
+ */
+
+#include <linux/module.h>
+#include <linux/list.h>
+#include <linux/net.h>
+#include <linux/in.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter_ipv4.h>
+#include <linux/netfilter_ipv6.h>
+#include <linux/spinlock.h>
+#include <net/sock.h>
+
+#include <asm/uaccess.h>
+
+#include "mesh_opts.h"
+
+#define MESH_SO_BASE_CTL MESH_SO_SET_TTL
+
+static struct list_head mesh_sks = LIST_HEAD_INIT(mesh_sks);
+static DEFINE_RWLOCK(mesh_sks_lock);
+
+struct mesh_sock {
+ struct list_head list;
+
+ struct sock *sk;
+ unsigned char ttl;
+ void (*orig_sk_destruct) (struct sock *sk);
+};
+
+static struct mesh_sock * lookup_socket(struct sock *sk)
+{
+ struct mesh_sock *mesh_sk;
+ struct mesh_sock *found_sk = NULL;
+
+ read_lock(&mesh_sks_lock);
+ list_for_each_entry(mesh_sk, &mesh_sks, list)
+ if (mesh_sk->sk == sk) {
+ found_sk = mesh_sk;
+ break;
+ }
+ read_unlock(&mesh_sks_lock);
+ return found_sk;
+}
+
+static void mesh_sk_destruct(struct sock *sk)
+{
+ struct mesh_sock *mesh_sk;
+ void (*orig_sk_destruct) (struct sock *sk);
+
+ mesh_sk = lookup_socket(sk);
+
+ if (mesh_sk) {
+ orig_sk_destruct = mesh_sk->orig_sk_destruct;
+ write_lock(&mesh_sks_lock);
+ list_del(&mesh_sk->list);
+ write_unlock(&mesh_sks_lock);
+ kfree(mesh_sk);
+ (*orig_sk_destruct)(sk);
+ }
+}
+
+static int do_mesh_set_mesh_ttl(struct sock *sk, void __user *user, unsigned int len)
+{
+ struct mesh_sock *mesh_sk;
+ unsigned char ttl;
+
+
+ if (len) {
+ if (get_user(ttl, (unsigned char *) user))
+ return -EFAULT;
+ } else
+ return -EINVAL;
+
+ mesh_sk = (struct mesh_sock*) kmalloc(sizeof(struct mesh_sock), GFP_KERNEL);
+ mesh_sk->ttl = ttl;
+ mesh_sk->sk = sk;
+ mesh_sk->orig_sk_destruct = sk->sk_destruct;
+ sk->sk_destruct = mesh_sk_destruct;
+ write_lock(&mesh_sks_lock);
+ list_add(&mesh_sk->list, &mesh_sks);
+ write_unlock(&mesh_sks_lock);
+
+ return 0;
+}
+
+static int do_mesh_get_mesh_ttl(struct sock *sk, void __user *user, int *len)
+{
+ struct mesh_sock *mesh_sk;
+ int rc = 0;
+
+ if ((mesh_sk = lookup_socket(sk)) == NULL)
+ return -ENODATA;
+
+ if (put_user(mesh_sk->ttl, (unsigned char*) user))
+ return -EFAULT;
+
+ /* netfilter wrapper does the copy to user of len */
+ *len = sizeof(unsigned char);
+
+ return rc;
+}
+
+static int do_mesh_set_ctl(struct sock *sk, int optval, void __user *user,
+ unsigned int len)
+{
+ return do_mesh_set_mesh_ttl(sk, user, len);
+}
+
+static int do_mesh_get_ctl(struct sock *sk, int optval, void __user *user,
+ int *len)
+{
+ return do_mesh_get_mesh_ttl(sk, user, len);
+}
+
+static unsigned char get_sock_mesh_ttl(struct sock *sk)
+{
+ struct mesh_sock *mesh_sk;
+
+ mesh_sk = lookup_socket(sk);
+
+ /* zero ttl results in using the network interface default */
+ return mesh_sk ? mesh_sk->ttl : 0;
+}
+
+static struct mesh_options mesh_opts =
+{
+ .get_sock_ttl = get_sock_mesh_ttl,
+};
+
+static struct nf_sockopt_ops mesh_sockopt_ops =
+{
+ .pf = PF_INET,
+ .set_optmin = MESH_SO_BASE_CTL,
+ .set_optmax = MESH_SO_BASE_CTL + 1,
+ .set = do_mesh_set_ctl,
+ .get_optmin = MESH_SO_BASE_CTL,
+ .get_optmax = MESH_SO_BASE_CTL + 1,
+ .get = do_mesh_get_ctl,
+};
+
+static int __init mesh_opts_init(void)
+{
+ int ret;
+
+ if ((ret = nf_register_sockopt(&mesh_sockopt_ops)) < 0) {
+ return ret;
+ }
+ libertas_register_mesh_opts(&mesh_opts);
+ return ret;
+}
+
+static void __exit mesh_opts_fini(void)
+{
+ nf_unregister_sockopt(&mesh_sockopt_ops);
+ libertas_unregister_mesh_opts(&mesh_opts);
+}
+
+module_init(mesh_opts_init);
+module_exit(mesh_opts_fini);
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/wireless/libertas/mesh_opts.h b/drivers/net/wireless/libertas/mesh_opts.h
new file mode 100644
index 0000000..cd18fb1
--- /dev/null
+++ b/drivers/net/wireless/libertas/mesh_opts.h
@@ -0,0 +1,5 @@
+
+struct mesh_options {
+ unsigned char (*get_sock_ttl)(struct sock*);
+};
+
--
1.4.4.2
More information about the libertas-dev
mailing list