From 43c2d3b5735ab692d335d6b59faf315bf854c707 Mon Sep 17 00:00:00 2001 From: Matthieu Coudron Date: Sat, 26 Oct 2013 18:16:30 +0200 Subject: [PATCH] Added basic tuntap support both on C and python sides --- include/linux/compiler.h | 313 +++++++++++++++++++++++++++++++++++ include/linux/if.h | 82 ++++++++- include/linux/if_tun.h | 101 +++++++++++ include/netlink/route/link/tuntap.h | 50 ++++++ lib/Makefile.am | 2 +- lib/route/link.c | 40 +++-- lib/route/link/tuntap.c | 306 ++++++++++++++++++++++++++++++++++ python/netlink/core.py | 5 +- python/netlink/route/address.py | 17 +- python/netlink/route/capi.i | 9 +- python/netlink/route/link.py | 10 +- python/netlink/route/links/tuntap.py | 22 +++ python/tests/link-create-tuntap.py | 107 ++++++++++++ python/tests/test.sh | 19 +++ 14 files changed, 1050 insertions(+), 33 deletions(-) create mode 100644 include/linux/compiler.h create mode 100644 include/linux/if_tun.h create mode 100644 include/netlink/route/link/tuntap.h create mode 100644 lib/route/link/tuntap.c create mode 100644 python/netlink/route/links/tuntap.py create mode 100644 python/tests/link-create-tuntap.py create mode 100755 python/tests/test.sh diff --git a/include/linux/compiler.h b/include/linux/compiler.h new file mode 100644 index 0000000..923d093 --- /dev/null +++ b/include/linux/compiler.h @@ -0,0 +1,313 @@ +#ifndef __LINUX_COMPILER_H +#define __LINUX_COMPILER_H + +#ifndef __ASSEMBLY__ + +#ifdef __CHECKER__ +# define __user __attribute__((noderef, address_space(1))) +# define __kernel __attribute__((address_space(0))) +# define __safe __attribute__((safe)) +# define __force __attribute__((force)) +# define __nocast __attribute__((nocast)) +# define __iomem __attribute__((noderef, address_space(2))) +# define __acquires(x) __attribute__((context(x,0,1))) +# define __releases(x) __attribute__((context(x,1,0))) +# define __acquire(x) __context__(x,1) +# define __release(x) __context__(x,-1) +# define __cond_lock(x,c) ((c) ? ({ __acquire(x); 1; }) : 0) +# define __percpu __attribute__((noderef, address_space(3))) +#ifdef CONFIG_SPARSE_RCU_POINTER +# define __rcu __attribute__((noderef, address_space(4))) +#else +# define __rcu +#endif +extern void __chk_user_ptr(const volatile void __user *); +extern void __chk_io_ptr(const volatile void __iomem *); +#else +# define __user +# define __kernel +# define __safe +# define __force +# define __nocast +# define __iomem +# define __chk_user_ptr(x) (void)0 +# define __chk_io_ptr(x) (void)0 +# define __builtin_warning(x, y...) (1) +# define __acquires(x) +# define __releases(x) +# define __acquire(x) (void)0 +# define __release(x) (void)0 +# define __cond_lock(x,c) (c) +# define __percpu +# define __rcu +#endif + +#ifdef __KERNEL__ + +#ifdef __GNUC__ +#include +#endif + +#define notrace __attribute__((no_instrument_function)) + +/* Intel compiler defines __GNUC__. So we will overwrite implementations + * coming from above header files here + */ +#ifdef __INTEL_COMPILER +# include +#endif + +/* + * Generic compiler-dependent macros required for kernel + * build go below this comment. Actual compiler/compiler version + * specific implementations come from the above header files + */ + +struct ftrace_branch_data { + const char *func; + const char *file; + unsigned line; + union { + struct { + unsigned long correct; + unsigned long incorrect; + }; + struct { + unsigned long miss; + unsigned long hit; + }; + unsigned long miss_hit[2]; + }; +}; + +/* + * Note: DISABLE_BRANCH_PROFILING can be used by special lowlevel code + * to disable branch tracing on a per file basis. + */ +#if defined(CONFIG_TRACE_BRANCH_PROFILING) \ + && !defined(DISABLE_BRANCH_PROFILING) && !defined(__CHECKER__) +void ftrace_likely_update(struct ftrace_branch_data *f, int val, int expect); + +#define likely_notrace(x) __builtin_expect(!!(x), 1) +#define unlikely_notrace(x) __builtin_expect(!!(x), 0) + +#define __branch_check__(x, expect) ({ \ + int ______r; \ + static struct ftrace_branch_data \ + __attribute__((__aligned__(4))) \ + __attribute__((section("_ftrace_annotated_branch"))) \ + ______f = { \ + .func = __func__, \ + .file = __FILE__, \ + .line = __LINE__, \ + }; \ + ______r = likely_notrace(x); \ + ftrace_likely_update(&______f, ______r, expect); \ + ______r; \ + }) + +/* + * Using __builtin_constant_p(x) to ignore cases where the return + * value is always the same. This idea is taken from a similar patch + * written by Daniel Walker. + */ +# ifndef likely +# define likely(x) (__builtin_constant_p(x) ? !!(x) : __branch_check__(x, 1)) +# endif +# ifndef unlikely +# define unlikely(x) (__builtin_constant_p(x) ? !!(x) : __branch_check__(x, 0)) +# endif + +#ifdef CONFIG_PROFILE_ALL_BRANCHES +/* + * "Define 'is'", Bill Clinton + * "Define 'if'", Steven Rostedt + */ +#define if(cond, ...) __trace_if( (cond , ## __VA_ARGS__) ) +#define __trace_if(cond) \ + if (__builtin_constant_p((cond)) ? !!(cond) : \ + ({ \ + int ______r; \ + static struct ftrace_branch_data \ + __attribute__((__aligned__(4))) \ + __attribute__((section("_ftrace_branch"))) \ + ______f = { \ + .func = __func__, \ + .file = __FILE__, \ + .line = __LINE__, \ + }; \ + ______r = !!(cond); \ + ______f.miss_hit[______r]++; \ + ______r; \ + })) +#endif /* CONFIG_PROFILE_ALL_BRANCHES */ + +#else +# define likely(x) __builtin_expect(!!(x), 1) +# define unlikely(x) __builtin_expect(!!(x), 0) +#endif + +/* Optimization barrier */ +#ifndef barrier +# define barrier() __memory_barrier() +#endif + +/* Unreachable code */ +#ifndef unreachable +# define unreachable() do { } while (1) +#endif + +#ifndef RELOC_HIDE +# define RELOC_HIDE(ptr, off) \ + ({ unsigned long __ptr; \ + __ptr = (unsigned long) (ptr); \ + (typeof(ptr)) (__ptr + (off)); }) +#endif + +#endif /* __KERNEL__ */ + +#endif /* __ASSEMBLY__ */ + +#ifdef __KERNEL__ +/* + * Allow us to mark functions as 'deprecated' and have gcc emit a nice + * warning for each use, in hopes of speeding the functions removal. + * Usage is: + * int __deprecated foo(void) + */ +#ifndef __deprecated +# define __deprecated /* unimplemented */ +#endif + +#ifdef MODULE +#define __deprecated_for_modules __deprecated +#else +#define __deprecated_for_modules +#endif + +#ifndef __must_check +#define __must_check +#endif + +#ifndef CONFIG_ENABLE_MUST_CHECK +#undef __must_check +#define __must_check +#endif +#ifndef CONFIG_ENABLE_WARN_DEPRECATED +#undef __deprecated +#undef __deprecated_for_modules +#define __deprecated +#define __deprecated_for_modules +#endif + +/* + * Allow us to avoid 'defined but not used' warnings on functions and data, + * as well as force them to be emitted to the assembly file. + * + * As of gcc 3.4, static functions that are not marked with attribute((used)) + * may be elided from the assembly file. As of gcc 3.4, static data not so + * marked will not be elided, but this may change in a future gcc version. + * + * NOTE: Because distributions shipped with a backported unit-at-a-time + * compiler in gcc 3.3, we must define __used to be __attribute__((used)) + * for gcc >=3.3 instead of 3.4. + * + * In prior versions of gcc, such functions and data would be emitted, but + * would be warned about except with attribute((unused)). + * + * Mark functions that are referenced only in inline assembly as __used so + * the code is emitted even though it appears to be unreferenced. + */ +#ifndef __used +# define __used /* unimplemented */ +#endif + +#ifndef __maybe_unused +# define __maybe_unused /* unimplemented */ +#endif + +#ifndef __always_unused +# define __always_unused /* unimplemented */ +#endif + +#ifndef noinline +#define noinline +#endif + +/* + * Rather then using noinline to prevent stack consumption, use + * noinline_for_stack instead. For documentation reasons. + */ +#define noinline_for_stack noinline + +#ifndef __always_inline +#define __always_inline inline +#endif + +#endif /* __KERNEL__ */ + +/* + * From the GCC manual: + * + * Many functions do not examine any values except their arguments, + * and have no effects except the return value. Basically this is + * just slightly more strict class than the `pure' attribute above, + * since function is not allowed to read global memory. + * + * Note that a function that has pointer arguments and examines the + * data pointed to must _not_ be declared `const'. Likewise, a + * function that calls a non-`const' function usually must not be + * `const'. It does not make sense for a `const' function to return + * `void'. + */ +#ifndef __attribute_const__ +# define __attribute_const__ /* unimplemented */ +#endif + +/* + * Tell gcc if a function is cold. The compiler will assume any path + * directly leading to the call is unlikely. + */ + +#ifndef __cold +#define __cold +#endif + +/* Simple shorthand for a section definition */ +#ifndef __section +# define __section(S) __attribute__ ((__section__(#S))) +#endif + +/* Are two types/vars the same type (ignoring qualifiers)? */ +#ifndef __same_type +# define __same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b)) +#endif + +/* Compile time object size, -1 for unknown */ +#ifndef __compiletime_object_size +# define __compiletime_object_size(obj) -1 +#endif +#ifndef __compiletime_warning +# define __compiletime_warning(message) +#endif +#ifndef __compiletime_error +# define __compiletime_error(message) +#endif +#ifndef __linktime_error +# define __linktime_error(message) +#endif +/* + * Prevent the compiler from merging or refetching accesses. The compiler + * is also forbidden from reordering successive instances of ACCESS_ONCE(), + * but only when the compiler is aware of some particular ordering. One way + * to make the compiler aware of ordering is to put the two invocations of + * ACCESS_ONCE() in different C statements. + * + * This macro does absolutely -nothing- to prevent the CPU from reordering, + * merging, or refetching absolutely anything at any time. Its main intended + * use is to mediate communication between process-level code and irq/NMI + * handlers, all running on the same CPU. + */ +#define ACCESS_ONCE(x) (*(volatile typeof(x) *)&(x)) + +#endif /* __LINUX_COMPILER_H */ diff --git a/include/linux/if.h b/include/linux/if.h index 238cf43..e9391d5 100644 --- a/include/linux/if.h +++ b/include/linux/if.h @@ -19,8 +19,14 @@ #ifndef _LINUX_IF_H #define _LINUX_IF_H +//#include /* for "__kernel_caddr_t" et al */ +//#include /* for "struct sockaddr" et al */ +#include /* for "__user" et al */ + + #define IFNAMSIZ 16 #define IFALIASZ 256 +#include /* Standard interface flags (netdevice->flags). */ #define IFF_UP 0x1 /* interface is up */ @@ -123,8 +129,29 @@ enum { IF_CARRIER_UP }; + + +struct if_settings { + unsigned int type; /* Type of physical device or protocol */ + unsigned int size; /* Size of the data allocated by the caller */ + union { + /* {atm/eth/dsl}_settings anyone ? */ + raw_hdlc_proto __user *raw_hdlc; + cisco_proto __user *cisco; + fr_proto __user *fr; + fr_proto_pvc __user *fr_pvc; + fr_proto_pvc_info __user *fr_pvc_info; + + /* interface settings */ + sync_serial_settings __user *sync; + te1_settings __user *te1; + } ifs_ifsu; +}; + + + /* - * Device mapping structure. I'd just gone off and designed a + * Device mapping structure. I'd just gone off and designed a * beautiful scheme using only loadable modules with arguments * for driver options and along come the PCMCIA people 8) * @@ -136,11 +163,62 @@ enum { struct ifmap { unsigned long mem_start; unsigned long mem_end; - unsigned short base_addr; + unsigned short base_addr; unsigned char irq; unsigned char dma; unsigned char port; /* 3 bytes spare */ }; + + +/* + * Interface request structure used for socket + * ioctl's. All interface ioctl's must have parameter + * definitions which begin with ifr_name. The + * remainder may be interface specific. + */ + +struct ifreq { +#define IFHWADDRLEN 6 + union + { + char ifrn_name[IFNAMSIZ]; /* if name, e.g. "en0" */ + } ifr_ifrn; + + union { + struct sockaddr ifru_addr; + struct sockaddr ifru_dstaddr; + struct sockaddr ifru_broadaddr; + struct sockaddr ifru_netmask; + struct sockaddr ifru_hwaddr; + short ifru_flags; + int ifru_ivalue; + int ifru_mtu; + struct ifmap ifru_map; + char ifru_slave[IFNAMSIZ]; /* Just fits the size */ + char ifru_newname[IFNAMSIZ]; + void __user * ifru_data; + struct if_settings ifru_settings; + } ifr_ifru; +}; +#define ifr_name ifr_ifrn.ifrn_name /* interface name */ +#define ifr_hwaddr ifr_ifru.ifru_hwaddr /* MAC address */ +#define ifr_addr ifr_ifru.ifru_addr /* address */ +#define ifr_dstaddr ifr_ifru.ifru_dstaddr /* other end of p-p lnk */ +#define ifr_broadaddr ifr_ifru.ifru_broadaddr /* broadcast address */ +#define ifr_netmask ifr_ifru.ifru_netmask /* interface net mask */ +#define ifr_flags ifr_ifru.ifru_flags /* flags */ +#define ifr_metric ifr_ifru.ifru_ivalue /* metric */ +#define ifr_mtu ifr_ifru.ifru_mtu /* mtu */ +#define ifr_map ifr_ifru.ifru_map /* device map */ +#define ifr_slave ifr_ifru.ifru_slave /* slave device */ +#define ifr_data ifr_ifru.ifru_data /* for use by interface */ +#define ifr_ifindex ifr_ifru.ifru_ivalue /* interface index */ +#define ifr_bandwidth ifr_ifru.ifru_ivalue /* link bandwidth */ +#define ifr_qlen ifr_ifru.ifru_ivalue /* Queue length */ +#define ifr_newname ifr_ifru.ifru_newname /* New name */ +#define ifr_settings ifr_ifru.ifru_settings /* Device/proto settings*/ + + #endif /* _LINUX_IF_H */ diff --git a/include/linux/if_tun.h b/include/linux/if_tun.h new file mode 100644 index 0000000..2835b85 --- /dev/null +++ b/include/linux/if_tun.h @@ -0,0 +1,101 @@ +/* + * Universal TUN/TAP device driver. + * Copyright (C) 1999-2000 Maxim Krasnyansky + * + * 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. + */ + +#ifndef _UAPI__IF_TUN_H +#define _UAPI__IF_TUN_H + +#include +#include +#include + +/* Read queue size */ +#define TUN_READQ_SIZE 500 + +/* TUN device flags */ +#define TUN_TUN_DEV 0x0001 +#define TUN_TAP_DEV 0x0002 +#define TUN_TYPE_MASK 0x000f + +#define TUN_FASYNC 0x0010 +#define TUN_NOCHECKSUM 0x0020 +#define TUN_NO_PI 0x0040 +/* This flag has no real effect */ +#define TUN_ONE_QUEUE 0x0080 +#define TUN_PERSIST 0x0100 +#define TUN_VNET_HDR 0x0200 +#define TUN_TAP_MQ 0x0400 + +/* Ioctl defines */ +#define TUNSETNOCSUM _IOW('T', 200, int) +#define TUNSETDEBUG _IOW('T', 201, int) +#define TUNSETIFF _IOW('T', 202, int) +#define TUNSETPERSIST _IOW('T', 203, int) +#define TUNSETOWNER _IOW('T', 204, int) +#define TUNSETLINK _IOW('T', 205, int) +#define TUNSETGROUP _IOW('T', 206, int) +#define TUNGETFEATURES _IOR('T', 207, unsigned int) +#define TUNSETOFFLOAD _IOW('T', 208, unsigned int) +#define TUNSETTXFILTER _IOW('T', 209, unsigned int) +#define TUNGETIFF _IOR('T', 210, unsigned int) +#define TUNGETSNDBUF _IOR('T', 211, int) +#define TUNSETSNDBUF _IOW('T', 212, int) +#define TUNATTACHFILTER _IOW('T', 213, struct sock_fprog) +#define TUNDETACHFILTER _IOW('T', 214, struct sock_fprog) +#define TUNGETVNETHDRSZ _IOR('T', 215, int) +#define TUNSETVNETHDRSZ _IOW('T', 216, int) +#define TUNSETQUEUE _IOW('T', 217, int) + +/* TUNSETIFF ifr flags */ +#define IFF_TUN 0x0001 +#define IFF_TAP 0x0002 +#define IFF_NO_PI 0x1000 +/* This flag has no real effect */ +#define IFF_ONE_QUEUE 0x2000 +#define IFF_VNET_HDR 0x4000 +#define IFF_TUN_EXCL 0x8000 +#define IFF_MULTI_QUEUE 0x0100 +#define IFF_ATTACH_QUEUE 0x0200 +#define IFF_DETACH_QUEUE 0x0400 + +/* Features for GSO (TUNSETOFFLOAD). */ +#define TUN_F_CSUM 0x01 /* You can hand me unchecksummed packets. */ +#define TUN_F_TSO4 0x02 /* I can handle TSO for IPv4 packets */ +#define TUN_F_TSO6 0x04 /* I can handle TSO for IPv6 packets */ +#define TUN_F_TSO_ECN 0x08 /* I can handle TSO with ECN bits. */ +#define TUN_F_UFO 0x10 /* I can handle UFO packets */ + +/* Protocol info prepended to the packets (when IFF_NO_PI is not set) */ +#define TUN_PKT_STRIP 0x0001 +struct tun_pi { + __u16 flags; + __be16 proto; +}; + +/* + * Filter spec (used for SETXXFILTER ioctls) + * This stuff is applicable only to the TAP (Ethernet) devices. + * If the count is zero the filter is disabled and the driver accepts + * all packets (promisc mode). + * If the filter is enabled in order to accept broadcast packets + * broadcast addr must be explicitly included in the addr list. + */ +#define TUN_FLT_ALLMULTI 0x0001 /* Accept all multicast packets */ +struct tun_filter { + __u16 flags; /* TUN_FLT_ flags see above */ + __u16 count; /* Number of addresses */ + __u8 addr[0][ETH_ALEN]; +}; + +#endif /* _UAPI__IF_TUN_H */ diff --git a/include/netlink/route/link/tuntap.h b/include/netlink/route/link/tuntap.h new file mode 100644 index 0000000..e316737 --- /dev/null +++ b/include/netlink/route/link/tuntap.h @@ -0,0 +1,50 @@ +/* + * netlink/route/link/tuntap.h Tuntap + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * Copyright (c) 2013 Matthieu Coudron + */ + +#ifndef NETLINK_LINK_TUNTAP_H_ +#define NETLINK_LINK_TUNTAP_H_ + + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +// Transform into an enum ? +#define TUNTAP_MODE_TUN 1 +#define TUNTAP_MODE_TAP 2 + +enum rtnl_link_tuntap_flags { + + RTNL_TUNTAP_PERSISTENT = TUNSETPERSIST +// IFF_MULTI_QUEUE +}; + +extern struct rtnl_link *rtnl_link_tuntap_alloc(void); + +int rtnl_link_is_tuntap(struct rtnl_link *link); + +int rtnl_link_tuntap_set_persistent(struct rtnl_link *link); +int rtnl_link_tuntap_set_mode(struct rtnl_link *link, int mode); +//int rtnl_link_tuntap_get_mode( struct rtnl_link *link ); + +int rtnl_link_tuntap_get_flags(struct rtnl_link *link); +int rtnl_link_tuntap_set_flags(struct rtnl_link *link, short flags); + +int rtnl_link_tuntap_set_user(struct rtnl_link *link, const char* user); +int rtnl_link_tuntap_set_group(struct rtnl_link *link, const char* group); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/Makefile.am b/lib/Makefile.am index 697683f..a2d2f43 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -76,7 +76,7 @@ libnl_route_3_la_SOURCES = \ route/link/api.c route/link/vlan.c route/link/dummy.c \ route/link/bridge.c route/link/inet6.c route/link/inet.c \ route/link/bonding.c route/link/can.c route/link/macvlan.c \ - route/link/vxlan.c \ + route/link/vxlan.c route/link/tuntap.c \ \ route/qdisc/blackhole.c route/qdisc/cbq.c route/qdisc/dsmark.c \ route/qdisc/fifo.c route/qdisc/htb.c route/qdisc/netem.c \ diff --git a/lib/route/link.c b/lib/route/link.c index a016899..838b8d4 100644 --- a/lib/route/link.c +++ b/lib/route/link.c @@ -310,7 +310,7 @@ static int link_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, err = -NLE_NOMEM; goto errout; } - + link->ce_msgtype = n->nlmsg_type; if (!nlmsg_valid_hdr(n, sizeof(*ifi))) @@ -391,9 +391,9 @@ static int link_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, */ struct rtnl_link_stats64 st; - nla_memcpy(&st, tb[IFLA_STATS64], + nla_memcpy(&st, tb[IFLA_STATS64], sizeof(struct rtnl_link_stats64)); - + link->l_stats[RTNL_LINK_RX_PACKETS] = st.rx_packets; link->l_stats[RTNL_LINK_TX_PACKETS] = st.tx_packets; link->l_stats[RTNL_LINK_RX_BYTES] = st.rx_bytes; @@ -473,7 +473,7 @@ static int link_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, } if (tb[IFLA_MAP]) { - nla_memcpy(&link->l_map, tb[IFLA_MAP], + nla_memcpy(&link->l_map, tb[IFLA_MAP], sizeof(struct rtnl_link_ifmap)); link->ce_mask |= LINK_ATTR_MAP; } @@ -738,7 +738,7 @@ static void link_dump_stats(struct nl_object *obj, struct nl_dump_params *p) struct rtnl_link *link = (struct rtnl_link *) obj; char *unit, fmt[64]; float res; - + link_dump_details(obj, p); nl_dump_line(p, " Stats: bytes packets errors " @@ -748,7 +748,7 @@ static void link_dump_stats(struct nl_object *obj, struct nl_dump_params *p) strcpy(fmt, " RX %X.2f %s %10" PRIu64 " %10" PRIu64 " %10" PRIu64 " %10" PRIu64 " %10" PRIu64 "\n"); fmt[9] = *unit == 'B' ? '9' : '7'; - + nl_dump_line(p, fmt, res, unit, link->l_stats[RTNL_LINK_RX_PACKETS], link->l_stats[RTNL_LINK_RX_ERRORS], @@ -760,7 +760,7 @@ static void link_dump_stats(struct nl_object *obj, struct nl_dump_params *p) strcpy(fmt, " TX %X.2f %s %10" PRIu64 " %10" PRIu64 " %10" PRIu64 " %10" PRIu64 " %10" PRIu64 "\n"); fmt[9] = *unit == 'B' ? '9' : '7'; - + nl_dump_line(p, fmt, res, unit, link->l_stats[RTNL_LINK_TX_PACKETS], link->l_stats[RTNL_LINK_TX_ERRORS], @@ -783,7 +783,7 @@ static void link_dump_stats(struct nl_object *obj, struct nl_dump_params *p) nl_dump_line(p, " aborted carrier heartbeat " " window collision\n"); - + nl_dump_line(p, " TX %10" PRIu64 " %10" PRIu64 " %10" PRIu64 " %10" PRIu64 " %10" PRIu64 "\n", link->l_stats[RTNL_LINK_TX_ABORT_ERR], @@ -988,13 +988,13 @@ int rtnl_link_alloc_cache(struct nl_sock *sk, int family, struct nl_cache **resu { struct nl_cache * cache; int err; - + cache = nl_cache_alloc(&rtnl_link_ops); if (!cache) return -NLE_NOMEM; cache->c_iarg1 = family; - + if (sk && (err = nl_cache_refill(sk, cache)) < 0) { nl_cache_free(cache); return err; @@ -1201,7 +1201,7 @@ int rtnl_link_name2i(struct nl_cache *cache, const char *name) { int ifindex = 0; struct rtnl_link *link; - + link = rtnl_link_get_by_name(cache, name); if (link) { ifindex = link->l_index; @@ -1358,7 +1358,7 @@ int rtnl_link_add(struct nl_sock *sk, struct rtnl_link *link, int flags) { struct nl_msg *msg; int err; - + err = rtnl_link_build_add_request(link, flags, &msg); if (err < 0) return err; @@ -1458,7 +1458,7 @@ int rtnl_link_change(struct nl_sock *sk, struct rtnl_link *orig, { struct nl_msg *msg; int err; - + err = rtnl_link_build_change_request(orig, changes, flags, &msg); if (err < 0) return err; @@ -1557,7 +1557,7 @@ int rtnl_link_delete(struct nl_sock *sk, const struct rtnl_link *link) { struct nl_msg *msg; int err; - + if ((err = rtnl_link_build_delete_request(link, &msg)) < 0) return err; @@ -2171,8 +2171,10 @@ int rtnl_link_set_type(struct rtnl_link *link, const char *type) if (link->l_info_ops) release_link_info(link); - if (!type) - return 0; + if (!type){ + NL_DBG(4,"Null string describing the type of link"); + return -NLE_FAILURE; + } kind = strdup(type); if (!kind) @@ -2185,6 +2187,10 @@ int rtnl_link_set_type(struct rtnl_link *link, const char *type) link->l_info_ops = io; } + else { + // TODO seems not to be called even if type not registered ? + NL_DBG(4,"Warning: no link type [%s] registered", type); + } link->l_info_kind = kind; link->ce_mask |= LINK_ATTR_LINKINFO; @@ -2338,7 +2344,7 @@ int rtnl_link_enslave_ifindex(struct nl_sock *sock, int master, int slave) rtnl_link_set_ifindex(link, slave); rtnl_link_set_master(link, master); - + if ((err = rtnl_link_change(sock, link, link, 0)) < 0) goto errout; diff --git a/lib/route/link/tuntap.c b/lib/route/link/tuntap.c new file mode 100644 index 0000000..e4b6716 --- /dev/null +++ b/lib/route/link/tuntap.c @@ -0,0 +1,306 @@ +/** + * @ingroup link + * @defgroup bridge Bridging + * + * @details + * @{ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + +struct tuntap_info { + struct ifreq tt_ifr; + int tt_fd; +}; + + + +/** @cond SKIP */ +#define IS_TUNTAP_LINK_ASSERT(link) \ + if ((link)->l_info_ops != &tuntap_info_ops) { \ + APPBUG("Link is not a tuntap link. set type \"tuntap\" first."); \ + return -NLE_OPNOTSUPP; \ + } +/** @endcond */ + + +/** + * Allocate link object of type bridge + * + * @return Allocated link object or NULL. + */ +struct rtnl_link *rtnl_link_tuntap_alloc(void) +{ + struct rtnl_link *link; + int err; + + if (!(link = rtnl_link_alloc())) + return NULL; + + if ((err = rtnl_link_set_type(link, "tuntap")) < 0) { + rtnl_link_put(link); + return NULL; + } + + return link; +} + + +// l_info est un void* +// expects a + +static int tun_alloc(struct rtnl_link * link) +{ + struct tuntap_info *ti; + char *name; + int err; + + if ((ti = calloc(1, sizeof(*ti))) == NULL) + return -NLE_NOMEM; + + link->l_info = ti; + + /* + TODO according to link flags, + + set mode TUN or tap + set persistent or not + set user or group + */ + + + if( ( ti->tt_fd = open("/dev/net/tun", O_RDWR)) < 0 ) { + // What is that ? tun_alloc_old(dev) + //NLE_OPNOTSUPP / NLE_NODEV / NLE_PERM + // TODO might + NL_DBG(4,"could not open /dev/net/tun in READ WRITE mode"); + return -NLE_NOACCESS ; + } + + memset(&(ti->tt_ifr), 0, sizeof( ti->tt_ifr)); + + /* Flags: IFF_TUN - TUN device (no Ethernet headers) + * IFF_TAP - TAP device + * + * IFF_NO_PI - Do not provide packet information + */ +// ti->tt_ifr.ifr_flags = IFF_TAP | IFF_NO_PI; + ti->tt_ifr.ifr_flags = IFF_TUN | IFF_NO_PI; + + + name = rtnl_link_get_name(link); + if( name ) { + NL_DBG(4,"Name of the tuntap set to [%s]", name ); + strncpy( ti->tt_ifr.ifr_name, name, IFNAMSIZ); + } + + + if( (err = ioctl(ti->tt_fd, TUNSETIFF, (void *) &(ti->tt_ifr) ) ) < 0 ){ + NL_DBG(1,"could not set ioctl TUNSETIFF"); + close(ti->tt_fd); + return err; + } + +// strcpy(dev, ifr.ifr_name); + return 0; +} + + +static void tuntap_free(struct rtnl_link *link) +{ + struct tuntap_info *ti = link->l_info; + + +// TODO close fd if not persistent ? +// if (ti) { +// free(vi->vi_egress_qos); +// vi->vi_egress_qos = NULL; +// } + + free(ti); + link->l_info = NULL; +} + + + + + +static void tuntap_dump_line(struct rtnl_link *link, struct nl_dump_params *p) +{ + struct tuntap_info *ti = link->l_info; + + nl_dump(p, "tuntap name %d", ti->tt_ifr.ifr_name); +} + +static void tuntap_dump_details(struct rtnl_link *link, struct nl_dump_params *p) +{ + // + tuntap_dump_line(link,p); +} + +static int tuntap_clone(struct rtnl_link *dst, struct rtnl_link *src) +{ + struct tuntap_info *tdst, *tsrc = src->l_info; + int err; + + dst->l_info = NULL; + if ((err = rtnl_link_set_type(dst, "tuntap")) < 0) + return err; + tdst = dst->l_info; + +// tdst->vi_egress_qos = calloc(vsrc->vi_egress_size, +// sizeof(struct vlan_map)); +// if (!vdst->vi_egress_qos) +// return -NLE_NOMEM; + +// memcpy(tdst->, vsrc->vi_egress_qos, +// vsrc->vi_egress_size * sizeof(struct vlan_map)); + + return 0; +} + +static struct rtnl_link_info_ops tuntap_info_ops = { + .io_name = "tuntap", + .io_alloc = tun_alloc, + //.io_parse = vlan_parse, + .io_dump[NL_DUMP_LINE] = tuntap_dump_line, + .io_dump[NL_DUMP_DETAILS] = tuntap_dump_details, + .io_free = tuntap_free, +}; + +static void __init tun_init(void) +{ + rtnl_link_register_info(&tuntap_info_ops); +} + +static void __exit tun_exit(void) +{ + rtnl_link_unregister_info(&tuntap_info_ops); +} + + + +int rtnl_link_tuntap_set_persistent(struct rtnl_link *link) { + + + struct tuntap_info *ti; + + IS_TUNTAP_LINK_ASSERT(link); + + ti = link->l_info; + + if (ioctl( ti->tt_fd, TUNSETPERSIST, 1) < 0) { + NL_DBG(1,"ioctl(TUNSETPERSIST)"); + return -NLE_FAILURE; + } + + + return 0; +} + + +int rtnl_link_tuntap_set_user(struct rtnl_link *link, const char* userId) { + + + uid_t uid; // basically an integer + struct passwd *pw; + struct tuntap_info *ti; + char* end; + IS_TUNTAP_LINK_ASSERT(link); + + if( userId == NULL) { + NL_DBG(1, "Null user id\n"); + return NLE_INVAL; + } + + /* if userId passed as an integer */ + if ( ((user = strtol(*userId, &end, 10)), !*end) ) { + + uid = user; + } + /* in case username is written as a string */ + else { + pw = getpwnam(userId); + if (!pw) { + NL_DBG(1, "invalid user \"%s\"\n", userId); + return (-NLE_INVAL; + } + + uid = pw->pw_uid; + + ti = link->l_info; + + if (uid != -1 && ioctl( ti->tt_fd, TUNSETOWNER, uid)) { + NL_DBG(1,"ioctl(TUNSETOWNER)\n"); + return -NLE_NOACCESS; + } + + return 0; +} + + + +int rtnl_link_tuntap_set_group(struct rtnl_link *link, const char* groupId) { + + gid_t gid; + unsigned long group; + + IS_TUNTAP_LINK_ASSERT(link); + + if( userId == NULL) { + NL_DBG(1, "Null group id\n"); + return NLE_INVAL; + } + + + if ((group = strtol(*argv, &end, 10)), !*end) { + gid = group; + } + else { + struct group *gr = getgrnam(*argv); + if (!gr) { + NL_DBG(1, "invalid group \"%s\"\n", *argv); + exit(-1); + } + *gid = gr->gr_gid; +} + + + +int rtnl_link_tuntap_get_flags(struct rtnl_link *link) { + + struct tuntap_info *ti = link->l_info; + + IS_TUNTAP_LINK_ASSERT(link); + + return ti->tt_ifr.ifr_flags; +} + +int rtnl_link_tuntap_set_flags(struct rtnl_link *link, short flags); + + +/** + * Check if link is a Tuntap link + * @arg link Link object + * + * @return True if link is a TunTaplink, otherwise false is returned. + */ +int rtnl_link_is_tuntap(struct rtnl_link *link) +{ + return (link->l_info_ops == &tuntap_info_ops ) ; + //&& !strcmp(link->l_info_ops->io_name, "tuntap"); +} + + diff --git a/python/netlink/core.py b/python/netlink/core.py index b301039..5bdbfcf 100644 --- a/python/netlink/core.py +++ b/python/netlink/core.py @@ -690,6 +690,7 @@ class AbstractAddress(object): """ def __init__(self, addr): + self._nl_addr = None if isinstance(addr, str): self._nl_addr = capi.addr_parse(addr, socket.AF_UNSPEC) if self._nl_addr is None: @@ -702,8 +703,8 @@ class AbstractAddress(object): elif addr: capi.nl_addr_get(addr) self._nl_addr = addr - else: - raise ValueError("Invalid value as addr. Expecting either nl_addr* or a string") + # else: + # raise ValueError("Invalid value [%s] as addr. Expecting either nl_addr* or a string" % type(addr)) diff --git a/python/netlink/route/address.py b/python/netlink/route/address.py index cab2a97..5d77620 100644 --- a/python/netlink/route/address.py +++ b/python/netlink/route/address.py @@ -26,8 +26,9 @@ class AddressCache(netlink.Cache): if not cache: cache = self._alloc_cache_name('route/addr') - self._protocol = netlink.NETLINK_ROUTE - self._nl_cache = cache + super().__init__(netlink.NETLINK_ROUTE, cache ) + # self._protocol = + # self._nl_cache = cache def __getitem__(self, key): # Using ifindex=0 here implies that the local address itself @@ -53,11 +54,14 @@ class AddressCache(netlink.Cache): def _new_cache(cache): return AddressCache(cache=cache) + + class Address(netlink.Object): """Network address""" def __init__(self, obj=None): - netlink.Object.__init__(self, 'route/addr', 'address', obj) + + super().__init__('route/addr', 'address', obj) self._rtnl_addr = self._obj2type(self._nl_object) @classmethod @@ -66,7 +70,7 @@ class Address(netlink.Object): @staticmethod def _obj2type(obj): - return capi.obj2addr(obj) + return capi.obj2rtaddr(obj) def __cmp__(self, other): # sort by: @@ -122,6 +126,7 @@ class Address(netlink.Object): # ifindex is immutable but we assume that if _orig does not # have an ifindex specified, it was meant to be given here if capi.rtnl_addr_get_ifindex(self._orig) == 0: + print("setting index to [%d]"%value.ifindex) capi.rtnl_addr_set_ifindex(self._orig, value.ifindex) @property @@ -206,7 +211,9 @@ class Address(netlink.Object): @local.setter def local(self, value): a = netlink.AbstractAddress(value) - capi.rtnl_addr_set_local(self._rtnl_addr, a._nl_addr) + ret = capi.rtnl_addr_set_local(self._rtnl_addr, a._nl_addr) + if ret != 0: + raise netlink.NetlinkError(ret) # local is immutable but we assume that if _orig does not # have a local address specified, it was meant to be given here diff --git a/python/netlink/route/capi.i b/python/netlink/route/capi.i index 9a420f6..490e555 100644 --- a/python/netlink/route/capi.i +++ b/python/netlink/route/capi.i @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -647,5 +648,9 @@ extern unsigned int rtnl_route_nh_get_flags (struct rtnl_nexthop *nh); extern char *rtnl_route_nh_flags2str (int flags, char *buf, size_t len); extern int rtnl_route_nh_str2flags (const char *name); -// purpose ? -//uint8_t rtnl_route_nh_get_weight (struct rtnl_nexthop *nh); + + +/* */ +extern int rtnl_link_is_tuntap(struct rtnl_link *link); +extern int rtnl_link_tuntap_set_user(struct rtnl_link *link, char* userId); +extern int rtnl_link_tuntap_set_persistent(struct rtnl_link *link); \ No newline at end of file diff --git a/python/netlink/route/link.py b/python/netlink/route/link.py index 8061637..74c7720 100644 --- a/python/netlink/route/link.py +++ b/python/netlink/route/link.py @@ -142,7 +142,7 @@ class Link(netlink.Object): """Network link""" def __init__(self, obj=None): - netlink.Object.__init__(self, 'route/link', 'link', obj) + super().__init__( 'route/link', 'link', obj) self._rtnl_link = self._obj2type(self._nl_object) if self.type: @@ -272,7 +272,8 @@ class Link(netlink.Object): @address.setter def address(self, value): - capi.rtnl_link_set_addr(self._rtnl_link, value._addr) + address = netlink.AbstractAddress(value) + capi.rtnl_link_set_addr(self._rtnl_link, address._nl_addr) @property @netlink.nlattr(type=str, fmt=util.addr) @@ -359,8 +360,9 @@ class Link(netlink.Object): @type.setter def type(self, value): - if capi.rtnl_link_set_type(self._rtnl_link, value) < 0: - raise NameError('unknown info type') + result = capi.rtnl_link_set_type(self._rtnl_link, value) + if result < 0: + raise NameError('unknown error [%d]'% result) self._module_lookup('netlink.route.links.' + value) diff --git a/python/netlink/route/links/tuntap.py b/python/netlink/route/links/tuntap.py new file mode 100644 index 0000000..072fdd1 --- /dev/null +++ b/python/netlink/route/links/tuntap.py @@ -0,0 +1,22 @@ +from __future__ import absolute_import + +from ... import core as netlink +from .. import capi as capi + + +# multiqueue +# enable queue +class TunTapLink(object): + def __init__(self, link): + self._link = link + # self._has_ext_info = capi.rtnl_link_bridge_has_ext_info(self._link) + # self._port_state_values = ['disabled','listening','learning','forwarding','blocking'] + + @staticmethod + def brief(): + return 'Tuntap' + + +def init(link): + link.tuntap = TunTapLink(link._rtnl_link) + return link.tuntap \ No newline at end of file diff --git a/python/tests/link-create-tuntap.py b/python/tests/link-create-tuntap.py new file mode 100644 index 0000000..952ff43 --- /dev/null +++ b/python/tests/link-create-tuntap.py @@ -0,0 +1,107 @@ +#!/usr/bin/python +import netlink.core as netlink +import netlink.route.capi as capi +import netlink.route.link as nl_link +import netlink.route.address as nl_addr +import os,pwd +import argparse +import logging + + +# Avoir une queue par XP +logger = logging.getLogger() +logger.setLevel(logging.DEBUG) + + +def getLogin(): + # Fails on some terminals + # username =os.getlogin() + # username = pwd.getpwuid(os.getuid())[0] + # if script was launched with sudo + username = os.getenv("SUDO_USER") or pwd.getpwuid(os.getuid())[0] + # logger.info("Username [%s]"% username ) + return username + + +# run tests +parser = argparse.ArgumentParser( + description='Will run tests you precise' + ) + + +# parser.add_argument('--simulate', action="store_true", help="Describe experiment settings") + +# parser.add_argument('remote_ip', help="IP of the other host of the experiment") + +parser.add_argument('action', choices=["add","del"], help='') +parser.add_argument('mode', choices=["tun","tap"], help='list of tests, their ') +parser.add_argument('name') + +# parse arguments ys.argv[1:] +args = parser.parse_args( ) + + # args.config_file + + +if_name = args.name + + +# First fill up the cache +sock = netlink.lookup_socket(netlink.NETLINK_ROUTE) + +cache = nl_link.LinkCache() +cache.refill(sock) + +# check if interface with name passed as argument exists + + +if args.action == "del": + try: + link = cache[ if_name ] + except Exception as e: + logger.error("No interface named [%s]: %s"% (if_name, e)) + exit(1) + + # if link.type == "tuntap": + if link.type == "tun": + link.delete() + else: + logger.error("Interface [%s] is not a tuntap but [%s]"%(if_name, link.type) ) + + + +# => action = add +else: +# testtap1 = cache['testtap1'] +# print(testtap1) + link = nl_link.Link() + link.name = if_name + username = getLogin() + + link.type = "tuntap" + logger.info("Link type [%s]"% link.type ) + + capi.rtnl_link_tuntap_set_user( link._rtnl_link , username ) + logger.info( "Address of the tuntap %s"% link.address ) + + + logger.info( "Setting tuntap permanent") + capi.rtnl_link_tuntap_set_persistent( link._rtnl_link ); + + addr = nl_addr.Address() + addr.local = "172.16.0.1/24" + addr.link = if_name + + logger.debug( addr ) + addr.add() + + + + + cache.refill() + link = cache[if_name] + + print( link ) + +os.system("ifconfig %s"%if_name) +# print( capi.rtnl_link_is_tuntap(lbr._rtnl_link) ) diff --git a/python/tests/test.sh b/python/tests/test.sh new file mode 100755 index 0000000..d144ca4 --- /dev/null +++ b/python/tests/test.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +folder=".." +bash -c "cd $folder && python3 setup.py build" +bash -c "cd $folder && sudo python3 setup.py install" + +if [ $# -lt 1 ]; then + echo "Use: $0 " + exit 1 +fi + +scriptFilename="$1" +shift +args="$@" +# for script in $@; do + +python3 "$scriptFilename" $args + +# done -- 1.8.1.2