[PATCH] libertas: Per-socket mesh-ttl via setsockopt().
Dan Williams
dcbw at redhat.com
Thu Jul 19 14:00:29 EDT 2007
On Mon, 2007-07-09 at 13:12 -0700, Javier Cardona wrote:
> 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>
Hmm; how does this fit in with the libertas driver? And where is
MESH_SO_SET_TTL defined?
Dan
> 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*);
> +};
> +
More information about the libertas-dev
mailing list