diff options
Diffstat (limited to 'python/netlink/route')
-rw-r--r-- | python/netlink/route/__init__.py | 0 | ||||
-rw-r--r-- | python/netlink/route/address.py | 398 | ||||
-rw-r--r-- | python/netlink/route/capi.i | 338 | ||||
-rw-r--r-- | python/netlink/route/link.py | 596 | ||||
-rw-r--r-- | python/netlink/route/links/__init__.py | 0 | ||||
-rw-r--r-- | python/netlink/route/links/dummy.py | 21 | ||||
-rw-r--r-- | python/netlink/route/links/inet.py | 153 | ||||
-rw-r--r-- | python/netlink/route/links/vlan.py | 62 | ||||
-rw-r--r-- | python/netlink/route/qdisc.py | 185 | ||||
-rw-r--r-- | python/netlink/route/tc.py | 357 |
10 files changed, 2110 insertions, 0 deletions
diff --git a/python/netlink/route/__init__.py b/python/netlink/route/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/python/netlink/route/__init__.py diff --git a/python/netlink/route/address.py b/python/netlink/route/address.py new file mode 100644 index 0000000..c0ce54f --- /dev/null +++ b/python/netlink/route/address.py @@ -0,0 +1,398 @@ +# +# Copyright (c) 2011 Thomas Graf <tgraf@suug.ch> +# + +"""Module providing access to network addresses +""" + +__version__ = "1.0" +__all__ = [ + 'AddressCache', + 'Address'] + +import datetime +import netlink.core as netlink +import netlink.capi as core_capi +import netlink.route.capi as capi +import netlink.route.link as Link +import netlink.util as util + +########################################################################### +# Address Cache +class AddressCache(netlink.Cache): + """Cache containing network addresses""" + + def __init__(self, cache=None): + if not cache: + cache = self._alloc_cache_name("route/addr") + + self._protocol = netlink.NETLINK_ROUTE + self._c_cache = cache + + def __getitem__(self, key): + # Using ifindex=0 here implies that the local address itself + # is unique, otherwise the first occurence is returned. + return self.lookup(0, key) + + def lookup(self, ifindex, local): + if type(local) is str: + local = netlink.AbstractAddress(local) + + addr = capi.rtnl_addr_get(self._c_cache, ifindex, + local._nl_addr) + if addr is None: + raise KeyError() + + return Address._from_capi(addr) + + def _new_object(self, obj): + return Address(obj) + + def _new_cache(self, cache): + return AddressCache(cache=cache) + +########################################################################### +# Address Object +class Address(netlink.Object): + """Network address""" + + def __init__(self, obj=None): + netlink.Object.__init__(self, "route/addr", "address", obj) + self._rtnl_addr = self._obj2type(self._nl_object) + + @classmethod + def _from_capi(cls, obj): + return cls(capi.addr2obj(obj)) + + def _obj2type(self, obj): + return capi.obj2addr(obj) + + def __cmp__(self, other): + # sort by: + # 1. network link + # 2. address family + # 3. local address (including prefixlen) + diff = self.ifindex - other.ifindex + + if diff == 0: + diff = self.family - other.family + if diff == 0: + diff = capi.nl_addr_cmp(self.local, other.local) + + return diff + + def _new_instance(self, obj): + return Address(obj) + + ##################################################################### + # ifindex + @netlink.nlattr('address.ifindex', type=int, immutable=True, + fmt=util.num) + @property + def ifindex(self): + """interface index""" + return capi.rtnl_addr_get_ifindex(self._rtnl_addr) + + @ifindex.setter + def ifindex(self, value): + link = Link.resolve(value) + if not link: + raise ValueError() + + self.link = link + + ##################################################################### + # link + @netlink.nlattr('address.link', type=str, fmt=util.string) + @property + def link(self): + link = capi.rtnl_addr_get_link(self._rtnl_addr) + if not link: + return None + + return Link.Link.from_capi(link) + + @link.setter + def link(self, value): + if type(value) is str: + try: + value = Link.resolve(value) + except KeyError: + raise ValueError() + + capi.rtnl_addr_set_link(self._rtnl_addr, value._rtnl_link) + + # 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: + capi.rtnl_addr_set_ifindex(self._orig, value.ifindex) + + ##################################################################### + # label + @netlink.nlattr('address.label', type=str, fmt=util.string) + @property + def label(self): + """address label""" + return capi.rtnl_addr_get_label(self._rtnl_addr) + + @label.setter + def label(self, value): + capi.rtnl_addr_set_label(self._rtnl_addr, value) + + ##################################################################### + # flags + @netlink.nlattr('address.flags', type=str, fmt=util.string) + @property + def flags(self): + """Flags""" + flags = capi.rtnl_addr_get_flags(self._rtnl_addr) + return capi.rtnl_addr_flags2str(flags, 256)[0].split(',') + + def _set_flag(self, flag): + if flag[0] == '-': + i = capi.rtnl_addr_str2flags(flag[1:]) + capi.rtnl_addr_unset_flags(self._rtnl_addr, i) + else: + i = capi.rtnl_addr_str2flags(flag[1:]) + capi.rtnl_addr_set_flags(self._rtnl_addr, i) + + @flags.setter + def flags(self, value): + if type(value) is list: + for flag in value: + self._set_flag(flag) + else: + self._set_flag(value) + + ##################################################################### + # family + @netlink.nlattr('address.family', type=int, immutable=True, + fmt=util.num) + @property + def family(self): + """Address family""" + fam = capi.rtnl_addr_get_family(self._rtnl_addr) + return netlink.AddressFamily(fam) + + @family.setter + def family(self, value): + if not isinstance(value, AddressFamily): + value = AddressFamily(value) + + capi.rtnl_addr_set_family(self._rtnl_addr, int(value)) + + ##################################################################### + # scope + @netlink.nlattr('address.scope', type=int, fmt=util.num) + @property + def scope(self): + """Address scope""" + scope = capi.rtnl_addr_get_scope(self._rtnl_addr) + return capi.rtnl_scope2str(scope, 32)[0] + + @scope.setter + def scope(self, value): + if type(value) is str: + value = capi.rtnl_str2scope(value) + capi.rtnl_addr_set_scope(self._rtnl_addr, value) + + ##################################################################### + # local address + @netlink.nlattr('address.local', type=str, immutable=True, + fmt=util.addr) + @property + def local(self): + """Local address""" + a = capi.rtnl_addr_get_local(self._rtnl_addr) + return netlink.AbstractAddress(a) + + @local.setter + def local(self, value): + a = netlink.AbstractAddress(value) + capi.rtnl_addr_set_local(self._rtnl_addr, a._nl_addr) + + # local is immutable but we assume that if _orig does not + # have a local address specified, it was meant to be given here + if capi.rtnl_addr_get_local(self._orig) is None: + capi.rtnl_addr_set_local(self._orig, a._nl_addr) + + ##################################################################### + # Peer address + @netlink.nlattr('address.peer', type=str, fmt=util.addr) + @property + def peer(self): + """Peer address""" + a = capi.rtnl_addr_get_peer(self._rtnl_addr) + return netlink.AbstractAddress(a) + + @peer.setter + def peer(self, value): + a = netlink.AbstractAddress(value) + capi.rtnl_addr_set_peer(self._rtnl_addr, a._nl_addr) + + ##################################################################### + # Broadcast address + @netlink.nlattr('address.broadcast', type=str, fmt=util.addr) + @property + def broadcast(self): + """Broadcast address""" + a = capi.rtnl_addr_get_broadcast(self._rtnl_addr) + return netlink.AbstractAddress(a) + + @broadcast.setter + def broadcast(self, value): + a = netlink.AbstractAddress(value) + capi.rtnl_addr_set_broadcast(self._rtnl_addr, a._nl_addr) + + ##################################################################### + # Multicast address + @netlink.nlattr('address.multicast', type=str, fmt=util.addr) + @property + def multicast(self): + """multicast address""" + a = capi.rtnl_addr_get_multicast(self._rtnl_addr) + return netlink.AbstractAddress(a) + + @multicast.setter + def multicast(self, value): + try: + a = netlink.AbstractAddress(value) + except ValueError as err: + raise AttributeError('multicast', err) + + capi.rtnl_addr_set_multicast(self._rtnl_addr, a._nl_addr) + + ##################################################################### + # Anycast address + @netlink.nlattr('address.anycast', type=str, fmt=util.addr) + @property + def anycast(self): + """anycast address""" + a = capi.rtnl_addr_get_anycast(self._rtnl_addr) + return netlink.AbstractAddress(a) + + @anycast.setter + def anycast(self, value): + a = netlink.AbstractAddress(value) + capi.rtnl_addr_set_anycast(self._rtnl_addr, a._nl_addr) + + ##################################################################### + # Valid lifetime + @netlink.nlattr('address.valid_lifetime', type=int, immutable=True, + fmt=util.num) + @property + def valid_lifetime(self): + """Valid lifetime""" + msecs = capi.rtnl_addr_get_valid_lifetime(self._rtnl_addr) + if msecs == 0xFFFFFFFF: + return None + else: + return datetime.timedelta(seconds=msecs) + + @valid_lifetime.setter + def valid_lifetime(self, value): + capi.rtnl_addr_set_valid_lifetime(self._rtnl_addr, int(value)) + + ##################################################################### + # Preferred lifetime + @netlink.nlattr('address.preferred_lifetime', type=int, + immutable=True, fmt=util.num) + @property + def preferred_lifetime(self): + """Preferred lifetime""" + msecs = capi.rtnl_addr_get_preferred_lifetime(self._rtnl_addr) + if msecs == 0xFFFFFFFF: + return None + else: + return datetime.timedelta(seconds=msecs) + + @preferred_lifetime.setter + def preferred_lifetime(self, value): + capi.rtnl_addr_set_preferred_lifetime(self._rtnl_addr, int(value)) + + ##################################################################### + # Creation Time + @netlink.nlattr('address.create_time', type=int, immutable=True, + fmt=util.num) + @property + def create_time(self): + """Creation time""" + hsec = capi.rtnl_addr_get_create_time(self._rtnl_addr) + return datetime.timedelta(milliseconds=10*hsec) + + ##################################################################### + # Last Update + @netlink.nlattr('address.last_update', type=int, immutable=True, + fmt=util.num) + @property + def last_update(self): + """Last update""" + hsec = capi.rtnl_addr_get_last_update_time(self._rtnl_addr) + return datetime.timedelta(milliseconds=10*hsec) + + ##################################################################### + # add() + def add(self, socket=None, flags=None): + if not socket: + socket = netlink.lookup_socket(netlink.NETLINK_ROUTE) + + if not flags: + flags = netlink.NLM_F_CREATE + + ret = capi.rtnl_addr_add(socket._sock, self._rtnl_addr, flags) + if ret < 0: + raise netlink.KernelError(ret) + + ##################################################################### + # delete() + def delete(self, socket): + """Attempt to delete this link in the kernel""" + ret = capi.rtnl_addr_delete(socket._sock, self._addr) + if ret < 0: + raise netlink.KernelError(ret) + + ################################################################### + # private properties + # + # Used for formatting output. USE AT OWN RISK + @property + def _flags(self): + return ','.join(self.flags) + + ################################################################### + # + # format(details=False, stats=False) + # + def format(self, details=False, stats=False, nodev=False, indent=''): + """Return address as formatted text""" + fmt = util.MyFormatter(self, indent) + + buf = fmt.format('{a|local!b}') + + if not nodev: + buf += fmt.format(' {a|ifindex}') + + buf += fmt.format(' {a|scope}') + + if self.label: + buf += fmt.format(' "{a|label}"') + + buf += fmt.format(' <{a|_flags}>') + + if details: + buf += fmt.nl('\t{t|broadcast} {t|multicast}') \ + + fmt.nl('\t{t|peer} {t|anycast}') + + if self.valid_lifetime: + buf += fmt.nl('\t{s|valid-lifetime!k} '\ + '{a|valid_lifetime}') + + if self.preferred_lifetime: + buf += fmt.nl('\t{s|preferred-lifetime!k} '\ + '{a|preferred_lifetime}') + + if stats and (self.create_time or self.last_update): + buf += self.nl('\t{s|created!k} {a|create_time}'\ + ' {s|last-updated!k} {a|last_update}') + + return buf diff --git a/python/netlink/route/capi.i b/python/netlink/route/capi.i new file mode 100644 index 0000000..abe3cc9 --- /dev/null +++ b/python/netlink/route/capi.i @@ -0,0 +1,338 @@ +%module capi +%{ +#include <netlink/route/rtnl.h> +#include <netlink/route/link.h> +#include <netlink/route/link/vlan.h> +#include <netlink/route/link/inet.h> + +#include <netlink/route/tc.h> +#include <netlink/route/qdisc.h> + +#include <netlink/route/addr.h> +%} + +%include <stdint.i> +%include <cstring.i> + +%inline %{ + struct nl_object *link2obj(struct rtnl_link *link) + { + return OBJ_CAST(link); + } + + struct rtnl_link *obj2link(struct nl_object *obj) + { + return (struct rtnl_link *) obj; + } + + struct rtnl_link *get_from_kernel(struct nl_sock *sk, int ifindex, const char *name) + { + struct rtnl_link *link; + if (rtnl_link_get_kernel(sk, ifindex, name, &link) < 0) + return NULL; + return link; + } + + uint32_t inet_get_conf(struct rtnl_link *link, const unsigned int id) + { + uint32_t result; + + if (rtnl_link_inet_get_conf(link, id, &result) < 0) + return 0; + + return result; + } +%}; + +extern struct nl_object *link2obj(struct rtnl_link *); +extern struct rtnl_link *obj2link(struct nl_object *); + +/* <netlink/route/rtnl.h> */ + +%cstring_output_maxsize(char *buf, size_t len) +extern char * rtnl_scope2str(int, char *buf, size_t len); +extern int rtnl_str2scope(const char *); + +/* <netlink/route/link.h> */ + +extern struct rtnl_link *rtnl_link_alloc(void); + +extern struct rtnl_link *rtnl_link_get(struct nl_cache *, int); +extern struct rtnl_link *rtnl_link_get_by_name(struct nl_cache *, const char *); + +extern int rtnl_link_build_add_request(struct rtnl_link *, int, struct nl_msg **); +extern int rtnl_link_add(struct nl_sock *, struct rtnl_link *, int); +extern int rtnl_link_build_change_request(struct rtnl_link *, struct rtnl_link *, int, struct nl_msg **); +extern int rtnl_link_change(struct nl_sock *, struct rtnl_link *, struct rtnl_link *, int); + +extern int rtnl_link_build_delete_request(const struct rtnl_link *, struct nl_msg **); +extern int rtnl_link_delete(struct nl_sock *, const struct rtnl_link *); +extern int rtnl_link_build_get_request(int, const char *, struct nl_msg **); + +extern char *rtnl_link_stat2str(int, char *, size_t); +extern int rtnl_link_str2stat(const char *); + +%cstring_output_maxsize(char *buf, size_t len) +extern char *rtnl_link_flags2str(int, char *buf, size_t len); +extern int rtnl_link_str2flags(const char *); + +%cstring_output_maxsize(char *buf, size_t len) +extern char *rtnl_link_operstate2str(uint8_t, char *buf, size_t len); +extern int rtnl_link_str2operstate(const char *); + +%cstring_output_maxsize(char *buf, size_t len) +extern char *rtnl_link_mode2str(uint8_t, char *buf, size_t len); +extern int rtnl_link_str2mode(const char *); + +extern void rtnl_link_set_qdisc(struct rtnl_link *, const char *); +extern char *rtnl_link_get_qdisc(struct rtnl_link *); + +extern void rtnl_link_set_name(struct rtnl_link *, const char *); +extern char *rtnl_link_get_name(struct rtnl_link *); + +extern void rtnl_link_set_flags(struct rtnl_link *, unsigned int); +extern void rtnl_link_unset_flags(struct rtnl_link *, unsigned int); +extern unsigned int rtnl_link_get_flags(struct rtnl_link *); + +extern void rtnl_link_set_mtu(struct rtnl_link *, unsigned int); +extern unsigned int rtnl_link_get_mtu(struct rtnl_link *); + +extern void rtnl_link_set_txqlen(struct rtnl_link *, unsigned int); +extern unsigned int rtnl_link_get_txqlen(struct rtnl_link *); + +extern void rtnl_link_set_weight(struct rtnl_link *, unsigned int); +extern unsigned int rtnl_link_get_weight(struct rtnl_link *); + +extern void rtnl_link_set_ifindex(struct rtnl_link *, int); +extern int rtnl_link_get_ifindex(struct rtnl_link *); + +extern void rtnl_link_set_family(struct rtnl_link *, int); +extern int rtnl_link_get_family(struct rtnl_link *); + +extern void rtnl_link_set_arptype(struct rtnl_link *, unsigned int); +extern unsigned int rtnl_link_get_arptype(struct rtnl_link *); + +extern void rtnl_link_set_addr(struct rtnl_link *, struct nl_addr *); +extern struct nl_addr *rtnl_link_get_addr(struct rtnl_link *); + +extern void rtnl_link_set_broadcast(struct rtnl_link *, struct nl_addr *); +extern struct nl_addr *rtnl_link_get_broadcast(struct rtnl_link *); + +extern void rtnl_link_set_link(struct rtnl_link *, int); +extern int rtnl_link_get_link(struct rtnl_link *); + +extern void rtnl_link_set_master(struct rtnl_link *, int); +extern int rtnl_link_get_master(struct rtnl_link *); + +extern void rtnl_link_set_operstate(struct rtnl_link *, uint8_t); +extern uint8_t rtnl_link_get_operstate(struct rtnl_link *); + +extern void rtnl_link_set_linkmode(struct rtnl_link *, uint8_t); +extern uint8_t rtnl_link_get_linkmode(struct rtnl_link *); + +extern const char *rtnl_link_get_ifalias(struct rtnl_link *); +extern void rtnl_link_set_ifalias(struct rtnl_link *, const char *); + +extern int rtnl_link_get_num_vf(struct rtnl_link *, uint32_t *); + +extern uint64_t rtnl_link_get_stat(struct rtnl_link *, int); +extern int rtnl_link_set_stat(struct rtnl_link *, const unsigned int, const uint64_t); + +extern int rtnl_link_set_info_type(struct rtnl_link *, const char *); +extern char *rtnl_link_get_info_type(struct rtnl_link *); + +/* <netlink/route/link/vlan.h> */ + +struct vlan_map +{ + uint32_t vm_from; + uint32_t vm_to; +}; + +#define VLAN_PRIO_MAX 7 + +%cstring_output_maxsize(char *buf, size_t len) +extern char *rtnl_link_vlan_flags2str(int, char *buf, size_t len); +extern int rtnl_link_vlan_str2flags(const char *); + +extern int rtnl_link_vlan_set_id(struct rtnl_link *, int); +extern int rtnl_link_vlan_get_id(struct rtnl_link *); + +extern int rtnl_link_vlan_set_flags(struct rtnl_link *, unsigned int); +extern int rtnl_link_vlan_unset_flags(struct rtnl_link *, unsigned int); +extern unsigned int rtnl_link_vlan_get_flags(struct rtnl_link *); + +extern int rtnl_link_vlan_set_ingress_map(struct rtnl_link *, int, uint32_t); +extern uint32_t *rtnl_link_vlan_get_ingress_map(struct rtnl_link *); + +extern int rtnl_link_vlan_set_egress_map(struct rtnl_link *, uint32_t, int); +extern struct vlan_map *rtnl_link_vlan_get_egress_map(struct rtnl_link *, int *); + +/* <netlink/route/link/inet.h> */ +%cstring_output_maxsize(char *buf, size_t len) +extern const char *rtnl_link_inet_devconf2str(int, char *buf, size_t len); +extern unsigned int rtnl_link_inet_str2devconf(const char *); + +extern int rtnl_link_inet_set_conf(struct rtnl_link *, const unsigned int, uint32_t); + +/* <netlink/route/tc.h> */ + +%inline %{ + uint32_t tc_str2handle(const char *name) + { + uint32_t result; + + if (rtnl_tc_str2handle(name, &result) < 0) + return 0; + + return result; + } +%}; + +extern void rtnl_tc_set_ifindex(struct rtnl_tc *, int); +extern int rtnl_tc_get_ifindex(struct rtnl_tc *); +extern void rtnl_tc_set_link(struct rtnl_tc *, struct rtnl_link *); +extern struct rtnl_link *rtnl_tc_get_link(struct rtnl_tc *); +extern void rtnl_tc_set_mtu(struct rtnl_tc *, uint32_t); +extern uint32_t rtnl_tc_get_mtu(struct rtnl_tc *); +extern void rtnl_tc_set_mpu(struct rtnl_tc *, uint32_t); +extern uint32_t rtnl_tc_get_mpu(struct rtnl_tc *); +extern void rtnl_tc_set_overhead(struct rtnl_tc *, uint32_t); +extern uint32_t rtnl_tc_get_overhead(struct rtnl_tc *); +extern void rtnl_tc_set_linktype(struct rtnl_tc *, uint32_t); +extern uint32_t rtnl_tc_get_linktype(struct rtnl_tc *); +extern void rtnl_tc_set_handle(struct rtnl_tc *, uint32_t); +extern uint32_t rtnl_tc_get_handle(struct rtnl_tc *); +extern void rtnl_tc_set_parent(struct rtnl_tc *, uint32_t); +extern uint32_t rtnl_tc_get_parent(struct rtnl_tc *); +extern int rtnl_tc_set_kind(struct rtnl_tc *, const char *); +extern char * rtnl_tc_get_kind(struct rtnl_tc *); +extern uint64_t rtnl_tc_get_stat(struct rtnl_tc *, enum rtnl_tc_stat); + +extern int rtnl_tc_calc_txtime(int, int); +extern int rtnl_tc_calc_bufsize(int, int); +extern int rtnl_tc_calc_cell_log(int); + +extern int rtnl_tc_read_classid_file(void); +%cstring_output_maxsize(char *buf, size_t len) +extern char * rtnl_tc_handle2str(uint32_t, char *buf, size_t len); +extern int rtnl_classid_generate(const char *, uint32_t *, uint32_t); + +/* <netlink/route/qdisc.h> */ + +%inline %{ + struct nl_object *qdisc2obj(struct rtnl_qdisc *qdisc) + { + return OBJ_CAST(qdisc); + } + + struct rtnl_qdisc *obj2qdisc(struct nl_object *obj) + { + return (struct rtnl_qdisc *) obj; + } + + struct rtnl_tc *obj2tc(struct nl_object *obj) + { + return TC_CAST(obj); + } +%}; +extern struct rtnl_qdisc * + rtnl_qdisc_alloc(void); + +extern struct rtnl_qdisc * + rtnl_qdisc_get(struct nl_cache *, int, uint32_t); + +extern struct rtnl_qdisc * + rtnl_qdisc_get_by_parent(struct nl_cache *, int, uint32_t); + +extern int rtnl_qdisc_build_add_request(struct rtnl_qdisc *, int, + struct nl_msg **); +extern int rtnl_qdisc_add(struct nl_sock *, struct rtnl_qdisc *, int); + +extern int rtnl_qdisc_build_update_request(struct rtnl_qdisc *, + struct rtnl_qdisc *, + int, struct nl_msg **); + +extern int rtnl_qdisc_update(struct nl_sock *, struct rtnl_qdisc *, + struct rtnl_qdisc *, int); + +extern int rtnl_qdisc_build_delete_request(struct rtnl_qdisc *, + struct nl_msg **); +extern int rtnl_qdisc_delete(struct nl_sock *, struct rtnl_qdisc *); + +/* <netlink/route/addr.h> */ + +%inline %{ + struct nl_object *addr2obj(struct rtnl_addr *addr) + { + return OBJ_CAST(addr); + } + + struct rtnl_addr *obj2addr(struct nl_object *obj) + { + return (struct rtnl_addr *) obj; + } +%}; + +extern struct rtnl_addr *rtnl_addr_alloc(void); + +extern struct rtnl_addr * + rtnl_addr_get(struct nl_cache *, int, struct nl_addr *); + +extern int rtnl_addr_build_add_request(struct rtnl_addr *, int, + struct nl_msg **); +extern int rtnl_addr_add(struct nl_sock *, struct rtnl_addr *, int); + +extern int rtnl_addr_build_delete_request(struct rtnl_addr *, int, + struct nl_msg **); +extern int rtnl_addr_delete(struct nl_sock *, + struct rtnl_addr *, int); + +%cstring_output_maxsize(char *buf, size_t len) +extern char * rtnl_addr_flags2str(int, char *buf, size_t len); +extern int rtnl_addr_str2flags(const char *); + +extern int rtnl_addr_set_label(struct rtnl_addr *, const char *); +extern char * rtnl_addr_get_label(struct rtnl_addr *); + +extern void rtnl_addr_set_ifindex(struct rtnl_addr *, int); +extern int rtnl_addr_get_ifindex(struct rtnl_addr *); + +extern void rtnl_addr_set_link(struct rtnl_addr *, struct rtnl_link *); +extern struct rtnl_link * + rtnl_addr_get_link(struct rtnl_addr *); +extern void rtnl_addr_set_family(struct rtnl_addr *, int); +extern int rtnl_addr_get_family(struct rtnl_addr *); + +extern void rtnl_addr_set_prefixlen(struct rtnl_addr *, int); +extern int rtnl_addr_get_prefixlen(struct rtnl_addr *); + +extern void rtnl_addr_set_scope(struct rtnl_addr *, int); +extern int rtnl_addr_get_scope(struct rtnl_addr *); + +extern void rtnl_addr_set_flags(struct rtnl_addr *, unsigned int); +extern void rtnl_addr_unset_flags(struct rtnl_addr *, unsigned int); +extern unsigned int rtnl_addr_get_flags(struct rtnl_addr *); + +extern int rtnl_addr_set_local(struct rtnl_addr *, + struct nl_addr *); +extern struct nl_addr *rtnl_addr_get_local(struct rtnl_addr *); + +extern int rtnl_addr_set_peer(struct rtnl_addr *, struct nl_addr *); +extern struct nl_addr *rtnl_addr_get_peer(struct rtnl_addr *); + +extern int rtnl_addr_set_broadcast(struct rtnl_addr *, struct nl_addr *); +extern struct nl_addr *rtnl_addr_get_broadcast(struct rtnl_addr *); + +extern int rtnl_addr_set_multicast(struct rtnl_addr *, struct nl_addr *); +extern struct nl_addr *rtnl_addr_get_multicast(struct rtnl_addr *); + +extern int rtnl_addr_set_anycast(struct rtnl_addr *, struct nl_addr *); +extern struct nl_addr *rtnl_addr_get_anycast(struct rtnl_addr *); + +extern uint32_t rtnl_addr_get_valid_lifetime(struct rtnl_addr *); +extern void rtnl_addr_set_valid_lifetime(struct rtnl_addr *, uint32_t); +extern uint32_t rtnl_addr_get_preferred_lifetime(struct rtnl_addr *); +extern void rtnl_addr_set_preferred_lifetime(struct rtnl_addr *, uint32_t); +extern uint32_t rtnl_addr_get_create_time(struct rtnl_addr *); +extern uint32_t rtnl_addr_get_last_update_time(struct rtnl_addr *); diff --git a/python/netlink/route/link.py b/python/netlink/route/link.py new file mode 100644 index 0000000..b8e19fa --- /dev/null +++ b/python/netlink/route/link.py @@ -0,0 +1,596 @@ +# +# Copyright (c) 2011 Thomas Graf <tgraf@suug.ch> +# + +"""Module providing access to network links + +This module provides an interface to view configured network links, +modify them and to add and delete virtual network links. + +The following is a basic example: + import netlink.core as netlink + import netlink.route.link as link + + sock = netlink.Socket() + sock.connect(netlink.NETLINK_ROUTE) + + cache = link.LinkCache() # create new empty link cache + cache.refill(sock) # fill cache with all configured links + eth0 = cache['eth0'] # lookup link "eth0" + print eth0 # print basic configuration + +The module contains the following public classes: + + - Link -- Represents a network link. Instances can be created directly + via the constructor (empty link objects) or via the refill() + method of a LinkCache. + - LinkCache -- Derived from netlink.Cache, holds any number of + network links (Link instances). Main purpose is to keep + a local list of all network links configured in the + kernel. + +The following public functions exist: + - get_from_kernel(socket, name) + +""" + +__version__ = "0.1" +__all__ = [ + 'LinkCache', + 'Link', + 'get_from_kernel'] + +import socket +import sys +import netlink.core as netlink +import netlink.capi as core_capi +import netlink.route.capi as capi +import netlink.route.links.inet as inet +import netlink.util as util + +########################################################################### +# Link statistics definitions +RX_PACKETS = 0 +TX_PACKETS = 1 +RX_BYTES = 2 +TX_BYTES = 3 +RX_ERRORS = 4 +TX_ERRORS = 5 +RX_DROPPED = 6 +TX_DROPPED = 7 +RX_COMPRESSED = 8 +TX_COMPRESSED = 9 +RX_FIFO_ERR = 10 +TX_FIFO_ERR = 11 +RX_LEN_ERR = 12 +RX_OVER_ERR = 13 +RX_CRC_ERR = 14 +RX_FRAME_ERR = 15 +RX_MISSED_ERR = 16 +TX_ABORT_ERR = 17 +TX_CARRIER_ERR = 18 +TX_HBEAT_ERR = 19 +TX_WIN_ERR = 20 +COLLISIONS = 21 +MULTICAST = 22 +IP6_INPKTS = 23 +IP6_INHDRERRORS = 24 +IP6_INTOOBIGERRORS = 25 +IP6_INNOROUTES = 26 +IP6_INADDRERRORS = 27 +IP6_INUNKNOWNPROTOS = 28 +IP6_INTRUNCATEDPKTS = 29 +IP6_INDISCARDS = 30 +IP6_INDELIVERS = 31 +IP6_OUTFORWDATAGRAMS = 32 +IP6_OUTPKTS = 33 +IP6_OUTDISCARDS = 34 +IP6_OUTNOROUTES = 35 +IP6_REASMTIMEOUT = 36 +IP6_REASMREQDS = 37 +IP6_REASMOKS = 38 +IP6_REASMFAILS = 39 +IP6_FRAGOKS = 40 +IP6_FRAGFAILS = 41 +IP6_FRAGCREATES = 42 +IP6_INMCASTPKTS = 43 +IP6_OUTMCASTPKTS = 44 +IP6_INBCASTPKTS = 45 +IP6_OUTBCASTPKTS = 46 +IP6_INOCTETS = 47 +IP6_OUTOCTETS = 48 +IP6_INMCASTOCTETS = 49 +IP6_OUTMCASTOCTETS = 50 +IP6_INBCASTOCTETS = 51 +IP6_OUTBCASTOCTETS = 52 +ICMP6_INMSGS = 53 +ICMP6_INERRORS = 54 +ICMP6_OUTMSGS = 55 +ICMP6_OUTERRORS = 56 + +########################################################################### +# Link Cache +class LinkCache(netlink.Cache): + """Cache of network links""" + + def __init__(self, family=socket.AF_UNSPEC, cache=None): + if not cache: + cache = self._alloc_cache_name("route/link") + + self._protocol = netlink.NETLINK_ROUTE + self._c_cache = cache + self._set_arg1(family) + + def __getitem__(self, key): + if type(key) is int: + link = capi.rtnl_link_get(self._c_cache, key) + elif type(key) is str: + link = capi.rtnl_link_get_by_name(self._c_cache, key) + + if link is None: + raise KeyError() + else: + return Link.from_capi(link) + + def _new_object(self, obj): + return Link(obj) + + def _new_cache(self, cache): + return LinkCache(family=self.arg1, cache=cache) + +########################################################################### +# Link Object +class Link(netlink.Object): + """Network link""" + + def __init__(self, obj=None): + netlink.Object.__init__(self, "route/link", "link", obj) + self._rtnl_link = self._obj2type(self._nl_object) + + self._type = None + + if self.type: + self._type_lookup(self.type) + + self.inet = inet.InetLink(self) + self.af = {'inet' : self.inet } + + @classmethod + def from_capi(cls, obj): + return cls(capi.link2obj(obj)) + + def _obj2type(self, obj): + return capi.obj2link(obj) + + def __cmp__(self, other): + return self.ifindex - other.ifindex + + def _new_instance(self, obj): + if not obj: + raise ValueError() + + return Link(obj) + + def _type_lookup(self, name): + rname = 'netlink.route.links.' + name + tmp = __import__(rname) + mod = sys.modules[rname] + + # this will create a type instance and assign it to + # link.<type>, e.g. link.vlan.id + self._type = mod.assign_type(self) + + ##################################################################### + # ifindex + @netlink.nlattr('link.ifindex', type=int, immutable=True, fmt=util.num) + @property + def ifindex(self): + """interface index""" + return capi.rtnl_link_get_ifindex(self._rtnl_link) + + @ifindex.setter + def ifindex(self, value): + capi.rtnl_link_set_ifindex(self._rtnl_link, int(value)) + + # 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_link_get_ifindex(self._orig) == 0: + capi.rtnl_link_set_ifindex(self._orig, int(value)) + + ##################################################################### + # name + @netlink.nlattr('link.name', type=str, fmt=util.bold) + @property + def name(self): + """Name of link""" + return capi.rtnl_link_get_name(self._rtnl_link) + + @name.setter + def name(self, value): + capi.rtnl_link_set_name(self._rtnl_link, value) + + # name is the secondary identifier, if _orig does not have + # the name specified yet, assume it was meant to be specified + # here. ifindex will always take priority, therefore if ifindex + # is specified as well, this will be ignored automatically. + if capi.rtnl_link_get_name(self._orig) is None: + capi.rtnl_link_set_name(self._orig, value) + + ##################################################################### + # flags + @netlink.nlattr('link.flags', type=str, fmt=util.string) + @property + def flags(self): + """Flags""" + flags = capi.rtnl_link_get_flags(self._rtnl_link) + return capi.rtnl_link_flags2str(flags, 256)[0].split(',') + + def _set_flag(self, flag): + if flag[0] == '-': + i = capi.rtnl_link_str2flags(flag[1:]) + capi.rtnl_link_unset_flags(self._rtnl_link, i) + else: + i = capi.rtnl_link_str2flags(flag[1:]) + capi.rtnl_link_set_flags(self._rtnl_link, i) + + @flags.setter + def flags(self, value): + if type(value) is list: + for flag in value: + self._set_flag(flag) + else: + self._set_flag(value) + + ##################################################################### + # mtu + @netlink.nlattr('link.mtu', type=int, fmt=util.num) + @property + def mtu(self): + """Maximum Transmission Unit""" + return capi.rtnl_link_get_mtu(self._rtnl_link) + + @mtu.setter + def mtu(self, value): + capi.rtnl_link_set_mtu(self._rtnl_link, int(value)) + + ##################################################################### + # family + @netlink.nlattr('link.family', type=int, immutable=True, fmt=util.num) + @property + def family(self): + """Address family""" + return capi.rtnl_link_get_family(self._rtnl_link) + + @family.setter + def family(self, value): + capi.rtnl_link_set_family(self._rtnl_link, value) + + ##################################################################### + # address + @netlink.nlattr('link.address', type=str, fmt=util.addr) + @property + def address(self): + """Hardware address (MAC address)""" + a = capi.rtnl_link_get_addr(self._rtnl_link) + return netlink.AbstractAddress(a) + + @address.setter + def address(self, value): + capi.rtnl_link_set_addr(self._rtnl_link, value._addr) + + ##################################################################### + # broadcast + @netlink.nlattr('link.broadcast', type=str, fmt=util.addr) + @property + def broadcast(self): + """Hardware broadcast address""" + a = capi.rtnl_link_get_broadcast(self._rtnl_link) + return netlink.AbstractAddress(a) + + @broadcast.setter + def broadcast(self, value): + capi.rtnl_link_set_broadcast(self._rtnl_link, value._addr) + + ##################################################################### + # qdisc + @netlink.nlattr('link.qdisc', type=str, immutable=True, fmt=util.string) + @property + def qdisc(self): + """Name of qdisc (cannot be changed)""" + return capi.rtnl_link_get_qdisc(self._rtnl_link) + + @qdisc.setter + def qdisc(self, value): + capi.rtnl_link_set_qdisc(self._rtnl_link, value) + + ##################################################################### + # txqlen + @netlink.nlattr('link.txqlen', type=int, fmt=util.num) + @property + def txqlen(self): + """"Length of transmit queue""" + return capi.rtnl_link_get_txqlen(self._rtnl_link) + + @txqlen.setter + def txqlen(self, value): + capi.rtnl_link_set_txqlen(self._rtnl_link, int(value)) + + ##################################################################### + # weight + @netlink.nlattr('link.weight', type=str, fmt=util.string) + @property + def weight(self): + """Weight""" + v = capi.rtnl_link_get_weight(self._rtnl_link) + if v == 4294967295: + return 'max' + else: + return str(v) + + @weight.setter + def weight(self, value): + if value == 'max': + v = 4294967295 + else: + v = int(value) + capi.rtnl_link_set_weight(self._rtnl_link, v) + + ##################################################################### + # arptype + @netlink.nlattr('link.arptype', type=str, immutable=True, fmt=util.string) + @property + def arptype(self): + """Type of link (cannot be changed)""" + type = capi.rtnl_link_get_arptype(self._rtnl_link) + return core_capi.nl_llproto2str(type, 64)[0] + + @arptype.setter + def arptype(self, value): + i = core_capi.nl_str2llproto(value) + capi.rtnl_link_set_arptype(self._rtnl_link, i) + + ##################################################################### + # operstate + @netlink.nlattr('link.operstate', type=str, immutable=True, + fmt=util.string, title='state') + @property + def operstate(self): + """Operational status""" + operstate = capi.rtnl_link_get_operstate(self._rtnl_link) + return capi.rtnl_link_operstate2str(operstate, 32)[0] + + @operstate.setter + def operstate(self, value): + i = capi.rtnl_link_str2operstate(flag) + capi.rtnl_link_set_operstate(self._rtnl_link, i) + + ##################################################################### + # mode + @netlink.nlattr('link.mode', type=str, immutable=True, fmt=util.string) + @property + def mode(self): + """Link mode""" + mode = capi.rtnl_link_get_linkmode(self._rtnl_link) + return capi.rtnl_link_mode2str(mode, 32)[0] + + @mode.setter + def mode(self, value): + i = capi.rtnl_link_str2mode(flag) + capi.rtnl_link_set_linkmode(self._rtnl_link, i) + + ##################################################################### + # alias + @netlink.nlattr('link.alias', type=str, fmt=util.string) + @property + def alias(self): + """Interface alias (SNMP)""" + return capi.rtnl_link_get_ifalias(self._rtnl_link) + + @alias.setter + def alias(self, value): + capi.rtnl_link_set_ifalias(self._rtnl_link, value) + + ##################################################################### + # type + @netlink.nlattr('link.type', type=str, fmt=util.string) + @property + def type(self): + """Link type""" + return capi.rtnl_link_get_info_type(self._rtnl_link) + + @type.setter + def type(self, value): + if capi.rtnl_link_set_info_type(self._rtnl_link, value) < 0: + raise NameError("unknown info type") + + self._type_lookup(value) + + ##################################################################### + # get_stat() + def get_stat(self, stat): + """Retrieve statistical information""" + if type(stat) is str: + stat = capi.rtnl_link_str2stat(stat) + if stat < 0: + raise NameError("unknown name of statistic") + + return capi.rtnl_link_get_stat(self._rtnl_link, stat) + + ##################################################################### + # add() + def add(self, socket=None, flags=None): + if not socket: + socket = netlink.lookup_socket(netlink.NETLINK_ROUTE) + + if not flags: + flags = netlink.NLM_F_CREATE + + ret = capi.rtnl_link_add(socket._sock, self._rtnl_link, flags) + if ret < 0: + raise netlink.KernelError(ret) + + ##################################################################### + # change() + def change(self, socket=None, flags=0): + """Commit changes made to the link object""" + if not socket: + socket = netlink.lookup_socket(netlink.NETLINK_ROUTE) + + if not self._orig: + raise NetlinkError("Original link not available") + ret = capi.rtnl_link_change(socket._sock, self._orig, self._rtnl_link, flags) + if ret < 0: + raise netlink.KernelError(ret) + + ##################################################################### + # delete() + def delete(self, socket=None): + """Attempt to delete this link in the kernel""" + if not socket: + socket = netlink.lookup_socket(netlink.NETLINK_ROUTE) + + ret = capi.rtnl_link_delete(socket._sock, self._rtnl_link) + if ret < 0: + raise netlink.KernelError(ret) + + ################################################################### + # private properties + # + # Used for formatting output. USE AT OWN RISK + @property + def _state(self): + if 'up' in self.flags: + buf = util.good('up') + if 'lowerup' not in self.flags: + buf += ' ' + util.bad('no-carrier') + else: + buf = util.bad('down') + return buf + + @property + def _brief(self): + buf = '' + if self.type: + if self._type and hasattr(self._type, 'brief'): + buf += self._type.brief() + else: + buf += util.yellow(self.type) + + return buf + self._foreach_af('brief') + + @property + def _flags(self): + ignore = ['up', 'running', 'lowerup'] + return ','.join([flag for flag in self.flags if flag not in ignore]) + + def _foreach_af(self, name, args=None): + buf = '' + for af in self.af: + try: + func = getattr(self.af[af], name) + s = str(func(args)) + if len(s) > 0: + buf += ' ' + s + except AttributeError: + pass + return buf + + ################################################################### + # + # format(details=False, stats=False) + # + def format(self, details=False, stats=False, indent=''): + """Return link as formatted text""" + fmt = util.MyFormatter(self, indent) + + buf = fmt.format('{a|ifindex} {a|name} {a|arptype} {a|address} '\ + '{a|_state} <{a|_flags}> {a|_brief}') + + if details: + buf += fmt.nl('\t{t|mtu} {t|txqlen} {t|weight} '\ + '{t|qdisc} {t|operstate}') + buf += fmt.nl('\t{t|broadcast} {t|alias}') + + buf += self._foreach_af('details', fmt) + + if stats: + l = [['Packets', RX_PACKETS, TX_PACKETS], + ['Bytes', RX_BYTES, TX_BYTES], + ['Errors', RX_ERRORS, TX_ERRORS], + ['Dropped', RX_DROPPED, TX_DROPPED], + ['Compressed', RX_COMPRESSED, TX_COMPRESSED], + ['FIFO Errors', RX_FIFO_ERR, TX_FIFO_ERR], + ['Length Errors', RX_LEN_ERR, None], + ['Over Errors', RX_OVER_ERR, None], + ['CRC Errors', RX_CRC_ERR, None], + ['Frame Errors', RX_FRAME_ERR, None], + ['Missed Errors', RX_MISSED_ERR, None], + ['Abort Errors', None, TX_ABORT_ERR], + ['Carrier Errors', None, TX_CARRIER_ERR], + ['Heartbeat Errors', None, TX_HBEAT_ERR], + ['Window Errors', None, TX_WIN_ERR], + ['Collisions', None, COLLISIONS], + ['Multicast', None, MULTICAST], + ['', None, None], + ['Ipv6:', None, None], + ['Packets', IP6_INPKTS, IP6_OUTPKTS], + ['Bytes', IP6_INOCTETS, IP6_OUTOCTETS], + ['Discards', IP6_INDISCARDS, IP6_OUTDISCARDS], + ['Multicast Packets', IP6_INMCASTPKTS, IP6_OUTMCASTPKTS], + ['Multicast Bytes', IP6_INMCASTOCTETS, IP6_OUTMCASTOCTETS], + ['Broadcast Packets', IP6_INBCASTPKTS, IP6_OUTBCASTPKTS], + ['Broadcast Bytes', IP6_INBCASTOCTETS, IP6_OUTBCASTOCTETS], + ['Delivers', IP6_INDELIVERS, None], + ['Forwarded', None, IP6_OUTFORWDATAGRAMS], + ['No Routes', IP6_INNOROUTES, IP6_OUTNOROUTES], + ['Header Errors', IP6_INHDRERRORS, None], + ['Too Big Errors', IP6_INTOOBIGERRORS, None], + ['Address Errors', IP6_INADDRERRORS, None], + ['Unknown Protocol', IP6_INUNKNOWNPROTOS, None], + ['Truncated Packets', IP6_INTRUNCATEDPKTS, None], + ['Reasm Timeouts', IP6_REASMTIMEOUT, None], + ['Reasm Requests', IP6_REASMREQDS, None], + ['Reasm Failures', IP6_REASMFAILS, None], + ['Reasm OK', IP6_REASMOKS, None], + ['Frag Created', None, IP6_FRAGCREATES], + ['Frag Failures', None, IP6_FRAGFAILS], + ['Frag OK', None, IP6_FRAGOKS], + ['', None, None], + ['ICMPv6:', None, None], + ['Messages', ICMP6_INMSGS, ICMP6_OUTMSGS], + ['Errors', ICMP6_INERRORS, ICMP6_OUTERRORS]] + + buf += '\n\t%s%s%s%s\n' % (33 * ' ', util.title('RX'), + 15 * ' ', util.title('TX')) + + for row in l: + row[0] = util.kw(row[0]) + row[1] = self.get_stat(row[1]) if row[1] else '' + row[2] = self.get_stat(row[2]) if row[2] else '' + buf += '\t{0:27} {1:>16} {2:>16}\n'.format(*row) + + buf += self._foreach_af('stats') + + return buf + +def get(name, socket=None): + """Lookup Link object directly from kernel""" + if not name: + raise ValueError() + + if not socket: + socket = netlink.lookup_socket(netlink.NETLINK_ROUTE) + + link = capi.get_from_kernel(socket._sock, 0, name) + if not link: + return None + + return Link.from_capi(link) + +link_cache = LinkCache() + +def resolve(name): + socket = netlink.lookup_socket(netlink.NETLINK_ROUTE) + link_cache.refill() + + return link_cache[name] diff --git a/python/netlink/route/links/__init__.py b/python/netlink/route/links/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/python/netlink/route/links/__init__.py diff --git a/python/netlink/route/links/dummy.py b/python/netlink/route/links/dummy.py new file mode 100644 index 0000000..495be94 --- /dev/null +++ b/python/netlink/route/links/dummy.py @@ -0,0 +1,21 @@ +# +# Copyright (c) 2011 Thomas Graf <tgraf@suug.ch> +# + +"""Dummy + +""" + +__version__ = "1.0" +__all__ = ['assign_type'] + +import netlink.core as netlink +import netlink.route.capi as capi + +class DummyLink(object): + def __init__(self, link): + self._rtnl_link = link + +def assign_type(link): + link.dummy = DummyLink(link._rtnl_link) + return link.dummy diff --git a/python/netlink/route/links/inet.py b/python/netlink/route/links/inet.py new file mode 100644 index 0000000..63c234f --- /dev/null +++ b/python/netlink/route/links/inet.py @@ -0,0 +1,153 @@ +# +# Copyright (c) 2011 Thomas Graf <tgraf@suug.ch> +# + +"""IPv4 + +""" + +__all__ = [''] + +import netlink.core as netlink +import netlink.route.capi as capi +import netlink.util as util + +DEVCONF_FORWARDING = 1 +DEVCONF_MC_FORWARDING = 2 +DEVCONF_PROXY_ARP = 3 +DEVCONF_ACCEPT_REDIRECTS = 4 +DEVCONF_SECURE_REDIRECTS = 5 +DEVCONF_SEND_REDIRECTS = 6 +DEVCONF_SHARED_MEDIA = 7 +DEVCONF_RP_FILTER = 8 +DEVCONF_ACCEPT_SOURCE_ROUTE = 9 +DEVCONF_BOOTP_RELAY = 10 +DEVCONF_LOG_MARTIANS = 11 +DEVCONF_TAG = 12 +DEVCONF_ARPFILTER = 13 +DEVCONF_MEDIUM_ID = 14 +DEVCONF_NOXFRM = 15 +DEVCONF_NOPOLICY = 16 +DEVCONF_FORCE_IGMP_VERSION = 17 +DEVCONF_ARP_ANNOUNCE = 18 +DEVCONF_ARP_IGNORE = 19 +DEVCONF_PROMOTE_SECONDARIES = 20 +DEVCONF_ARP_ACCEPT = 21 +DEVCONF_ARP_NOTIFY = 22 +DEVCONF_ACCEPT_LOCAL = 23 +DEVCONF_SRC_VMARK = 24 +DEVCONF_PROXY_ARP_PVLAN = 25 +DEVCONF_MAX = DEVCONF_PROXY_ARP_PVLAN + +def _resolve(id): + if type(id) is str: + id = capi.rtnl_link_inet_str2devconf(id)[0] + if id < 0: + raise NameError("unknown configuration id") + return id + +class InetLink(object): + def __init__(self, link): + self._link = link + + def details(self, fmt): + buf = '\n' + fmt.nl('\t%s\n\t' % util.title('Configuration:')) + + for i in range(DEVCONF_FORWARDING,DEVCONF_MAX+1): + if i & 1 and i > 1: + buf += fmt.nl('\t') + txt = util.kw(capi.rtnl_link_inet_devconf2str(i, 32)[0]) + buf += fmt.format('{0:28s} {1:12} ', txt, + self.get_conf(i)) + + + return buf + + def get_conf(self, id): + return capi.inet_get_conf(self._link._rtnl_link, _resolve(id)) + + def set_conf(self, id, value): + return capi.rtnl_link_inet_set_conf(self._link._rtnl_link, + _resolve(id), int(value)) + + @netlink.nlattr('link.inet.forwarding', type=bool, fmt=util.bool) + @property + def forwarding(self): + return bool(self.get_conf(DEVCONF_FORWARDING)) + + @forwarding.setter + def forwarding(self, value): + self.set_conf(DEVCONF_FORWARDING, int(value)) + + @netlink.nlattr('link.inet.mc_forwarding', type=bool, fmt=util.bool) + @property + def mc_forwarding(self): + return bool(self.get_conf(DEVCONF_MC_FORWARDING)) + + @mc_forwarding.setter + def mc_forwarding(self, value): + self.set_conf(DEVCONF_MC_FORWARDING, int(value)) + + @netlink.nlattr('link.inet.proxy_arp', type=bool, fmt=util.bool) + @property + def proxy_arp(self): + return bool(self.get_conf(DEVCONF_PROXY_ARP)) + + @proxy_arp.setter + def proxy_arp(self, value): + self.set_conf(DEVCONF_PROXY_ARP, int(value)) + + @netlink.nlattr('link.inet.accept_redirects', type=bool, fmt=util.bool) + @property + def accept_redirects(self): + return bool(self.get_conf(DEVCONF_ACCEPT_REDIRECTS)) + + @accept_redirects.setter + def accept_redirects(self, value): + self.set_conf(DEVCONF_ACCEPT_REDIRECTS, int(value)) + + @netlink.nlattr('link.inet.secure_redirects', type=bool, fmt=util.bool) + @property + def secure_redirects(self): + return bool(self.get_conf(DEVCONF_SECURE_REDIRECTS)) + + @secure_redirects.setter + def secure_redirects(self, value): + self.set_conf(DEVCONF_SECURE_REDIRECTS, int(value)) + + @netlink.nlattr('link.inet.send_redirects', type=bool, fmt=util.bool) + @property + def send_redirects(self): + return bool(self.get_conf(DEVCONF_SEND_REDIRECTS)) + + @send_redirects.setter + def send_redirects(self, value): + self.set_conf(DEVCONF_SEND_REDIRECTS, int(value)) + + @netlink.nlattr('link.inet.shared_media', type=bool, fmt=util.bool) + @property + def shared_media(self): + return bool(self.get_conf(DEVCONF_SHARED_MEDIA)) + + @shared_media.setter + def shared_media(self, value): + self.set_conf(DEVCONF_SHARED_MEDIA, int(value)) + +# IPV4_DEVCONF_RP_FILTER, +# IPV4_DEVCONF_ACCEPT_SOURCE_ROUTE, +# IPV4_DEVCONF_BOOTP_RELAY, +# IPV4_DEVCONF_LOG_MARTIANS, +# IPV4_DEVCONF_TAG, +# IPV4_DEVCONF_ARPFILTER, +# IPV4_DEVCONF_MEDIUM_ID, +# IPV4_DEVCONF_NOXFRM, +# IPV4_DEVCONF_NOPOLICY, +# IPV4_DEVCONF_FORCE_IGMP_VERSION, +# IPV4_DEVCONF_ARP_ANNOUNCE, +# IPV4_DEVCONF_ARP_IGNORE, +# IPV4_DEVCONF_PROMOTE_SECONDARIES, +# IPV4_DEVCONF_ARP_ACCEPT, +# IPV4_DEVCONF_ARP_NOTIFY, +# IPV4_DEVCONF_ACCEPT_LOCAL, +# IPV4_DEVCONF_SRC_VMARK, +# IPV4_DEVCONF_PROXY_ARP_PVLAN, diff --git a/python/netlink/route/links/vlan.py b/python/netlink/route/links/vlan.py new file mode 100644 index 0000000..1d4e03d --- /dev/null +++ b/python/netlink/route/links/vlan.py @@ -0,0 +1,62 @@ +# +# Copyright (c) 2011 Thomas Graf <tgraf@suug.ch> +# + +"""VLAN network link + +""" + +import netlink.core as netlink +import netlink.route.capi as capi + +class VLANLink(object): + def __init__(self, link): + self._link = link + + ################################################################### + # id + @netlink.nlattr('link.vlan.id', type=int) + @property + def id(self): + """vlan identifier""" + return capi.rtnl_link_vlan_get_id(self._link) + + @id.setter + def id(self, value): + capi.rtnl_link_vlan_set_id(self._link, int(value)) + + ################################################################### + # flags + @netlink.nlattr('link.vlan.flags', type=str) + @property + def flags(self): + """vlan flags""" + flags = capi.rtnl_link_vlan_get_flags(self._link) + return capi.rtnl_link_vlan_flags2str(flags, 256)[0].split(',') + + def _set_flag(self, flag): + i = capi.rtnl_link_vlan_str2flags(flag[1:]) + if flag[0] == '-': + capi.rtnl_link_vlan_unset_flags(self._link, i) + else: + capi.rtnl_link_vlan_set_flags(self._link, i) + + @flags.setter + def flags(self, value): + if type(value) is list: + for flag in value: + self._set_flag(flag) + else: + self._set_flag(value) + + ################################################################### + # TODO: + # - ingress map + # - egress map + + def brief(self): + return 'vlan-id ' + self.id + +def assign_type(link): + link.vlan = VLANLink(link._link) + return link.vlan diff --git a/python/netlink/route/qdisc.py b/python/netlink/route/qdisc.py new file mode 100644 index 0000000..c8a4c6f --- /dev/null +++ b/python/netlink/route/qdisc.py @@ -0,0 +1,185 @@ +# +# Copyright (c) 2011 Thomas Graf <tgraf@suug.ch> +# + +__all__ = [ + 'QdiscCache', + 'Qdisc'] + +import netlink.core as netlink +import netlink.capi as core_capi +import netlink.route.capi as capi +import netlink.util as util +import netlink.route.tc as tc + +########################################################################### +# Link Cache +class QdiscCache(netlink.Cache): + """Cache of qdiscs""" + + def __init__(self, cache=None): + if not cache: + cache = self._alloc_cache_name("route/qdisc") + + self._protocol = netlink.NETLINK_ROUTE + self._c_cache = cache + +# def __getitem__(self, key): +# if type(key) is int: +# link = capi.rtnl_link_get(self._this, key) +# elif type(key) is str: +# link = capi.rtnl_link_get_by_name(self._this, key) +# +# if qdisc is None: +# raise KeyError() +# else: +# return Qdisc._from_capi(capi.qdisc2obj(qdisc)) + + def _new_object(self, obj): + return Qdisc(obj) + + def _new_cache(self, cache): + return QdiscCache(cache=cache) + +########################################################################### +# Qdisc Object +class Qdisc(tc.Tc): + """Network link""" + + def __init__(self, obj=None): + self._name = "qdisc" + self._abbr = "qdisc" + + netlink.Object.__init__(self, "route/qdisc", "qdisc", obj) + self._tc = capi.obj2tc(self._nl_object) + self._rtnl_qdisc = self._obj2type(self._nl_object) + + netlink.attr('qdisc.handle', fmt=util.handle) + netlink.attr('qdisc.parent', fmt=util.handle) + netlink.attr('qdisc.kind', fmt=util.bold) + + def _obj2type(self, obj): + return capi.obj2qdisc(obj) + + def __cmp__(self, other): + return self.handle - other.handle + + def _new_instance(self, obj): + if not obj: + raise ValueError() + + return Qdisc(obj) + +# ##################################################################### +# # add() +# def add(self, socket, flags=None): +# if not flags: +# flags = netlink.NLM_F_CREATE +# +# ret = capi.rtnl_link_add(socket._sock, self._link, flags) +# if ret < 0: +# raise netlink.KernelError(ret) +# +# ##################################################################### +# # change() +# def change(self, socket, flags=0): +# """Commit changes made to the link object""" +# if not self._orig: +# raise NetlinkError("Original link not available") +# ret = capi.rtnl_link_change(socket._sock, self._orig, self._link, flags) +# if ret < 0: +# raise netlink.KernelError(ret) +# +# ##################################################################### +# # delete() +# def delete(self, socket): +# """Attempt to delete this link in the kernel""" +# ret = capi.rtnl_link_delete(socket._sock, self._link) +# if ret < 0: +# raise netlink.KernelError(ret) + + @property + def _dev(self): + buf = util.kw('dev') + ' ' + + if self.link: + return buf + util.string(self.link.name) + else: + return buf + util.num(self.ifindex) + + @property + def _parent(self): + return util.kw('parent') + ' ' + str(self.parent) + + ################################################################### + # + # format(details=False, stats=False) + # + def format(self, details=False, stats=False): + """Return qdisc as formatted text""" + fmt = util.MyFormatter(self) + + buf = fmt.format('qdisc {kind} {handle} {_dev} {_parent}') + + if details: + fmt = util.MyFormatter(self) + buf += fmt.format('\n'\ + '\t{mtu} {mpu} {overhead}\n') + +# if stats: +# l = [['Packets', RX_PACKETS, TX_PACKETS], +# ['Bytes', RX_BYTES, TX_BYTES], +# ['Errors', RX_ERRORS, TX_ERRORS], +# ['Dropped', RX_DROPPED, TX_DROPPED], +# ['Compressed', RX_COMPRESSED, TX_COMPRESSED], +# ['FIFO Errors', RX_FIFO_ERR, TX_FIFO_ERR], +# ['Length Errors', RX_LEN_ERR, None], +# ['Over Errors', RX_OVER_ERR, None], +# ['CRC Errors', RX_CRC_ERR, None], +# ['Frame Errors', RX_FRAME_ERR, None], +# ['Missed Errors', RX_MISSED_ERR, None], +# ['Abort Errors', None, TX_ABORT_ERR], +# ['Carrier Errors', None, TX_CARRIER_ERR], +# ['Heartbeat Errors', None, TX_HBEAT_ERR], +# ['Window Errors', None, TX_WIN_ERR], +# ['Collisions', None, COLLISIONS], +# ['Multicast', None, MULTICAST], +# ['', None, None], +# ['Ipv6:', None, None], +# ['Packets', IP6_INPKTS, IP6_OUTPKTS], +# ['Bytes', IP6_INOCTETS, IP6_OUTOCTETS], +# ['Discards', IP6_INDISCARDS, IP6_OUTDISCARDS], +# ['Multicast Packets', IP6_INMCASTPKTS, IP6_OUTMCASTPKTS], +# ['Multicast Bytes', IP6_INMCASTOCTETS, IP6_OUTMCASTOCTETS], +# ['Broadcast Packets', IP6_INBCASTPKTS, IP6_OUTBCASTPKTS], +# ['Broadcast Bytes', IP6_INBCASTOCTETS, IP6_OUTBCASTOCTETS], +# ['Delivers', IP6_INDELIVERS, None], +# ['Forwarded', None, IP6_OUTFORWDATAGRAMS], +# ['No Routes', IP6_INNOROUTES, IP6_OUTNOROUTES], +# ['Header Errors', IP6_INHDRERRORS, None], +# ['Too Big Errors', IP6_INTOOBIGERRORS, None], +# ['Address Errors', IP6_INADDRERRORS, None], +# ['Unknown Protocol', IP6_INUNKNOWNPROTOS, None], +# ['Truncated Packets', IP6_INTRUNCATEDPKTS, None], +# ['Reasm Timeouts', IP6_REASMTIMEOUT, None], +# ['Reasm Requests', IP6_REASMREQDS, None], +# ['Reasm Failures', IP6_REASMFAILS, None], +# ['Reasm OK', IP6_REASMOKS, None], +# ['Frag Created', None, IP6_FRAGCREATES], +# ['Frag Failures', None, IP6_FRAGFAILS], +# ['Frag OK', None, IP6_FRAGOKS], +# ['', None, None], +# ['ICMPv6:', None, None], +# ['Messages', ICMP6_INMSGS, ICMP6_OUTMSGS], +# ['Errors', ICMP6_INERRORS, ICMP6_OUTERRORS]] +# +# buf += '\n\t%s%s%s%s\n' % (33 * ' ', util.title('RX'), +# 15 * ' ', util.title('TX')) +# +# for row in l: +# row[0] = util.kw(row[0]) +# row[1] = self.get_stat(row[1]) if row[1] else '' +# row[2] = self.get_stat(row[2]) if row[2] else '' +# buf += '\t{0:27} {1:>16} {2:>16}\n'.format(*row) + + return buf diff --git a/python/netlink/route/tc.py b/python/netlink/route/tc.py new file mode 100644 index 0000000..ebe25e1 --- /dev/null +++ b/python/netlink/route/tc.py @@ -0,0 +1,357 @@ + + +# +# Copyright (c) 2011 Thomas Graf <tgraf@suug.ch> +# + +__all__ = [ + 'TcCache', + 'Tc', + 'QdiscCache', + 'Qdisc'] + +import socket +import sys +import netlink.core as netlink +import netlink.capi as core_capi +import netlink.route.capi as capi +import netlink.util as util + +from netlink.route.link import Link + +TC_PACKETS = 0 +TC_BYTES = 1 +TC_RATE_BPS = 2 +TC_RATE_PPS = 3 +TC_QLEN = 4 +TC_BACKLOG = 5 +TC_DROPS = 6 +TC_REQUEUES = 7 +TC_OVERLIMITS = 9 + +TC_H_ROOT = 0xFFFFFFFF +TC_H_INGRESS = 0xFFFFFFF1 + +class Handle(object): + def __init__(self, val=None): + if type(val) is str: + val = capi.tc_str2handle(val) + elif not val: + val = 0 + + self._val = int(val) + + def __int__(self): + return self._val + + def __str__(self): + return capi.rtnl_tc_handle2str(self._val, 64)[0] + + def isroot(self): + return self._val == TC_H_ROOT or self._val == TC_H_INGRESS + +########################################################################### +# TC Cache +class TcCache(netlink.Cache): + """Cache of traffic control object""" + + def __getitem__(self, key): + raise NotImplementedError() + +########################################################################### +# Tc Object +class Tc(netlink.Object): + def __cmp__(self, other): + return self.ifindex - other.ifindex + + def isroot(self): + return self.parent.isroot() + + ##################################################################### + # ifindex + @property + def ifindex(self): + """interface index""" + return capi.rtnl_tc_get_ifindex(self._tc) + + @ifindex.setter + def ifindex(self, value): + capi.rtnl_tc_set_ifindex(self._tc, int(value)) + + ##################################################################### + # link + @property + def link(self): + link = capi.rtnl_tc_get_link(self._tc) + if not link: + return None + else: + return Link._from_capi(link) + + @link.setter + def link(self, value): + capi.rtnl_tc_set_link(self._tc, value._link) + + ##################################################################### + # mtu + @property + def mtu(self): + return capi.rtnl_tc_get_mtu(self._tc) + + @mtu.setter + def mtu(self, value): + capi.rtnl_tc_set_mtu(self._tc, int(value)) + + ##################################################################### + # mpu + @property + def mpu(self): + return capi.rtnl_tc_get_mpu(self._tc) + + @mpu.setter + def mpu(self, value): + capi.rtnl_tc_set_mpu(self._tc, int(value)) + + ##################################################################### + # overhead + @property + def overhead(self): + return capi.rtnl_tc_get_overhead(self._tc) + + @overhead.setter + def overhead(self, value): + capi.rtnl_tc_set_overhead(self._tc, int(value)) + + ##################################################################### + # linktype + @property + def linktype(self): + return capi.rtnl_tc_get_linktype(self._tc) + + @linktype.setter + def linktype(self, value): + capi.rtnl_tc_set_linktype(self._tc, int(value)) + + ##################################################################### + # handle + @property + def handle(self): + return Handle(capi.rtnl_tc_get_handle(self._tc)) + + @handle.setter + def handle(self, value): + capi.rtnl_tc_set_handle(self._tc, int(value)) + + ##################################################################### + # parent + @property + def parent(self): + return Handle(capi.rtnl_tc_get_parent(self._tc)) + + @parent.setter + def parent(self, value): + capi.rtnl_tc_set_parent(self._tc, int(value)) + + ##################################################################### + # kind + @property + def kind(self): + return capi.rtnl_tc_get_kind(self._tc) + + @kind.setter + def kind(self, value): + capi.rtnl_tc_set_kind(self._tc, value) + + def get_stat(self, id): + return capi.rtnl_tc_get_stat(self._tc, id) + +class TcTree(object): + def __init__(self, link, sock): + self._qdisc_cache = QdiscCache().refill(sock) + + def __getitem__(self, key): + pass +# if type(key) is int: +# link = capi.rtnl_link_get(self._this, key) +# elif type(key) is str: +# link = capi.rtnl_link_get_by_name(self._this, key) +# +# if qdisc is None: +# raise KeyError() +# else: +# return Qdisc._from_capi(capi.qdisc2obj(qdisc)) + + + + +########################################################################### +# Link Cache +class QdiscCache(netlink.Cache): + """Cache of qdiscs""" + + def __init__(self, cache=None): + if not cache: + cache = self._alloc_cache_name("route/qdisc") + + self._c_cache = cache + +# def __getitem__(self, key): +# if type(key) is int: +# link = capi.rtnl_link_get(self._this, key) +# elif type(key) is str: +# link = capi.rtnl_link_get_by_name(self._this, key) +# +# if qdisc is None: +# raise KeyError() +# else: +# return Qdisc._from_capi(capi.qdisc2obj(qdisc)) + + def _new_object(self, obj): + return Qdisc(obj) + + def _new_cache(self, cache): + return QdiscCache(cache=cache) + +########################################################################### +# Qdisc Object +class Qdisc(Tc): + """Network link""" + + def __init__(self, obj=None): + self._name = "qdisc" + self._abbr = "qdisc" + + if not obj: + self._qdisc = capi.rtnl_qdisc_alloc() + else: + self._qdisc = capi.obj2qdisc(obj) + + self._obj = capi.qdisc2obj(self._qdisc) + self._orig = capi.obj2qdisc(core_capi.nl_object_clone(self._obj)) + + Tc.__init__(self) + + netlink.attr('qdisc.handle', fmt=util.handle) + netlink.attr('qdisc.parent', fmt=util.handle) + netlink.attr('qdisc.kind', fmt=util.bold) + + def __cmp__(self, other): + return self.handle - other.handle + + def _new_instance(self, obj): + if not obj: raise ValueError() + return Qdisc(obj) + +# ##################################################################### +# # add() +# def add(self, socket, flags=None): +# if not flags: +# flags = netlink.NLM_F_CREATE +# +# ret = capi.rtnl_link_add(socket._sock, self._link, flags) +# if ret < 0: +# raise netlink.KernelError(ret) +# +# ##################################################################### +# # change() +# def change(self, socket, flags=0): +# """Commit changes made to the link object""" +# if not self._orig: +# raise NetlinkError("Original link not available") +# ret = capi.rtnl_link_change(socket._sock, self._orig, self._link, flags) +# if ret < 0: +# raise netlink.KernelError(ret) +# +# ##################################################################### +# # delete() +# def delete(self, socket): +# """Attempt to delete this link in the kernel""" +# ret = capi.rtnl_link_delete(socket._sock, self._link) +# if ret < 0: +# raise netlink.KernelError(ret) + + @property + def _dev(self): + buf = util.kw('dev') + ' ' + + if self.link: + return buf + util.string(self.link.name) + else: + return buf + util.num(self.ifindex) + + @property + def _parent(self): + return util.kw('parent') + ' ' + str(self.parent) + + ################################################################### + # + # format(details=False, stats=False) + # + def format(self, details=False, stats=False): + """Return qdisc as formatted text""" + fmt = util.BriefFormatter(self) + + buf = fmt.format('qdisc {kind} {handle} {_dev} {_parent}') + + if details: + fmt = util.DetailFormatter(self) + buf += fmt.format('\n'\ + '\t{mtu} {mpu} {overhead}\n') + +# if stats: +# l = [['Packets', RX_PACKETS, TX_PACKETS], +# ['Bytes', RX_BYTES, TX_BYTES], +# ['Errors', RX_ERRORS, TX_ERRORS], +# ['Dropped', RX_DROPPED, TX_DROPPED], +# ['Compressed', RX_COMPRESSED, TX_COMPRESSED], +# ['FIFO Errors', RX_FIFO_ERR, TX_FIFO_ERR], +# ['Length Errors', RX_LEN_ERR, None], +# ['Over Errors', RX_OVER_ERR, None], +# ['CRC Errors', RX_CRC_ERR, None], +# ['Frame Errors', RX_FRAME_ERR, None], +# ['Missed Errors', RX_MISSED_ERR, None], +# ['Abort Errors', None, TX_ABORT_ERR], +# ['Carrier Errors', None, TX_CARRIER_ERR], +# ['Heartbeat Errors', None, TX_HBEAT_ERR], +# ['Window Errors', None, TX_WIN_ERR], +# ['Collisions', None, COLLISIONS], +# ['Multicast', None, MULTICAST], +# ['', None, None], +# ['Ipv6:', None, None], +# ['Packets', IP6_INPKTS, IP6_OUTPKTS], +# ['Bytes', IP6_INOCTETS, IP6_OUTOCTETS], +# ['Discards', IP6_INDISCARDS, IP6_OUTDISCARDS], +# ['Multicast Packets', IP6_INMCASTPKTS, IP6_OUTMCASTPKTS], +# ['Multicast Bytes', IP6_INMCASTOCTETS, IP6_OUTMCASTOCTETS], +# ['Broadcast Packets', IP6_INBCASTPKTS, IP6_OUTBCASTPKTS], +# ['Broadcast Bytes', IP6_INBCASTOCTETS, IP6_OUTBCASTOCTETS], +# ['Delivers', IP6_INDELIVERS, None], +# ['Forwarded', None, IP6_OUTFORWDATAGRAMS], +# ['No Routes', IP6_INNOROUTES, IP6_OUTNOROUTES], +# ['Header Errors', IP6_INHDRERRORS, None], +# ['Too Big Errors', IP6_INTOOBIGERRORS, None], +# ['Address Errors', IP6_INADDRERRORS, None], +# ['Unknown Protocol', IP6_INUNKNOWNPROTOS, None], +# ['Truncated Packets', IP6_INTRUNCATEDPKTS, None], +# ['Reasm Timeouts', IP6_REASMTIMEOUT, None], +# ['Reasm Requests', IP6_REASMREQDS, None], +# ['Reasm Failures', IP6_REASMFAILS, None], +# ['Reasm OK', IP6_REASMOKS, None], +# ['Frag Created', None, IP6_FRAGCREATES], +# ['Frag Failures', None, IP6_FRAGFAILS], +# ['Frag OK', None, IP6_FRAGOKS], +# ['', None, None], +# ['ICMPv6:', None, None], +# ['Messages', ICMP6_INMSGS, ICMP6_OUTMSGS], +# ['Errors', ICMP6_INERRORS, ICMP6_OUTERRORS]] +# +# buf += '\n\t%s%s%s%s\n' % (33 * ' ', util.title('RX'), +# 15 * ' ', util.title('TX')) +# +# for row in l: +# row[0] = util.kw(row[0]) +# row[1] = self.get_stat(row[1]) if row[1] else '' +# row[2] = self.get_stat(row[2]) if row[2] else '' +# buf += '\t{0:27} {1:>16} {2:>16}\n'.format(*row) + + return buf |