diff --git a/lib/Makefile.am b/lib/Makefile.am index c1277be..4aafb3b 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -59,7 +59,7 @@ libnl_route_la_SOURCES = \ route/cls/ematch/meta.c \ \ route/link/api.c route/link/vlan.c \ - route/link/bridge.c \ + route/link/bridge.c route/link/inet6.c \ \ route/sch/blackhole.c route/sch/cbq.c route/sch/dsmark.c \ route/sch/fifo.c route/sch/htb.c route/sch/netem.c route/sch/prio.c \ diff --git a/lib/route/link/inet6.c b/lib/route/link/inet6.c new file mode 100644 index 0000000..e368404 --- /dev/null +++ b/lib/route/link/inet6.c @@ -0,0 +1,327 @@ +/* + * lib/route/link/inet6.c AF_INET6 link operations + * + * 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) 2010 Thomas Graf + */ + +#include +#include +#include +#include +#include + +struct inet6_data +{ + uint32_t i6_flags; + struct ifla_cacheinfo i6_cacheinfo; + uint32_t i6_conf[DEVCONF_MAX]; +}; + +static void *inet6_alloc(struct rtnl_link *link) +{ + return calloc(1, sizeof(struct inet6_data)); +} + +static void *inet6_clone(struct rtnl_link *link, void *data) +{ + struct inet6_data *i6; + + if ((i6 = inet6_alloc(link))) + memcpy(i6, data, sizeof(*i6)); + + return i6; +} + +static void inet6_free(struct rtnl_link *link, void *data) +{ + free(data); +} + +static struct nla_policy inet6_policy[IFLA_INET6_MAX+1] = { + [IFLA_INET6_FLAGS] = { .type = NLA_U32 }, + [IFLA_INET6_CACHEINFO] = { .minlen = sizeof(struct ifla_cacheinfo) }, + [IFLA_INET6_CONF] = { .minlen = DEVCONF_MAX * 4 }, + [IFLA_INET6_STATS] = { .minlen = __IPSTATS_MIB_MAX * 8 }, + [IFLA_INET6_ICMP6STATS] = { .minlen = __ICMP6_MIB_MAX * 8 }, +}; + +static int inet6_parse_protinfo(struct rtnl_link *link, struct nlattr *attr, + void *data) +{ + struct inet6_data *i6 = data; + struct nlattr *tb[IFLA_INET6_MAX+1]; + int err; + + err = nla_parse_nested(tb, IFLA_INET6_MAX, attr, inet6_policy); + if (err < 0) + return err; + + if (tb[IFLA_INET6_FLAGS]) + i6->i6_flags = nla_get_u32(tb[IFLA_INET6_FLAGS]); + + if (tb[IFLA_INET6_CACHEINFO]) + nla_memcpy(&i6->i6_cacheinfo, tb[IFLA_INET6_CACHEINFO], + sizeof(i6->i6_cacheinfo)); + + if (tb[IFLA_INET6_CONF]) + nla_memcpy(&i6->i6_conf, tb[IFLA_INET6_CONF], + sizeof(i6->i6_conf)); + + if (tb[IFLA_INET6_STATS]) { + uint64_t *cnt = nla_data(tb[IFLA_INET6_STATS]); + int i; + + for (i = 1; i < __IPSTATS_MIB_MAX; i++) + rtnl_link_set_stat(link, RTNL_LINK_INPKTS, cnt[i-1]); + } + + if (tb[IFLA_INET6_ICMP6STATS]) { + uint64_t *cnt = nla_data(tb[IFLA_INET6_STATS]); + int i; + + for (i = 1; i < __ICMP6_MIB_MAX; i++) + rtnl_link_set_stat(link, RTNL_LINK_ICMP6_INMSGS, cnt[i-1]); + } + + return 0; +} + +/* These live in include/net/if_inet6.h and should be moved to include/linux */ +#define IF_RA_OTHERCONF 0x80 +#define IF_RA_MANAGED 0x40 +#define IF_RA_RCVD 0x20 +#define IF_RS_SENT 0x10 +#define IF_READY 0x80000000 + +static struct trans_tbl inet6_flags[] = { + __ADD(IF_RA_OTHERCONF, ra_otherconf) + __ADD(IF_RA_MANAGED, ra_managed) + __ADD(IF_RA_RCVD, ra_rcvd) + __ADD(IF_RS_SENT, rs_sent) + __ADD(IF_READY, ready) +}; + +static char *inet6_flags2str(int flags, char *buf, size_t len) +{ + return __flags2str(flags, buf, len, inet6_flags, + ARRAY_SIZE(inet6_flags)); +} + +static struct trans_tbl inet6_devconf[] = { + __ADD(DEVCONF_FORWARDING, forwarding) + __ADD(DEVCONF_HOPLIMIT, hoplimit) + __ADD(DEVCONF_MTU6, mtu6) + __ADD(DEVCONF_ACCEPT_RA, accept_ra) + __ADD(DEVCONF_ACCEPT_REDIRECTS, accept_redirects) + __ADD(DEVCONF_AUTOCONF, autoconf) + __ADD(DEVCONF_DAD_TRANSMITS, dad_transmits) + __ADD(DEVCONF_RTR_SOLICITS, rtr_solicits) + __ADD(DEVCONF_RTR_SOLICIT_INTERVAL, rtr_solicit_interval) + __ADD(DEVCONF_RTR_SOLICIT_DELAY, rtr_solicit_delay) + __ADD(DEVCONF_USE_TEMPADDR, use_tempaddr) + __ADD(DEVCONF_TEMP_VALID_LFT, temp_valid_lft) + __ADD(DEVCONF_TEMP_PREFERED_LFT, temp_prefered_lft) + __ADD(DEVCONF_REGEN_MAX_RETRY, regen_max_retry) + __ADD(DEVCONF_MAX_DESYNC_FACTOR, max_desync_factor) + __ADD(DEVCONF_MAX_ADDRESSES, max_addresses) + __ADD(DEVCONF_FORCE_MLD_VERSION, force_mld_version) + __ADD(DEVCONF_ACCEPT_RA_DEFRTR, accept_ra_defrtr) + __ADD(DEVCONF_ACCEPT_RA_PINFO, accept_ra_pinfo) + __ADD(DEVCONF_ACCEPT_RA_RTR_PREF, accept_ra_rtr_pref) + __ADD(DEVCONF_RTR_PROBE_INTERVAL, rtr_probe_interval) + __ADD(DEVCONF_ACCEPT_RA_RT_INFO_MAX_PLEN, accept_ra_rt_info) + __ADD(DEVCONF_PROXY_NDP, proxy_ndp) + __ADD(DEVCONF_OPTIMISTIC_DAD, optimistic_dad) + __ADD(DEVCONF_ACCEPT_SOURCE_ROUTE, accept_source_route) + __ADD(DEVCONF_MC_FORWARDING, mc_forwarding) + __ADD(DEVCONF_DISABLE_IPV6, disable_ipv6) + __ADD(DEVCONF_ACCEPT_DAD, accept_dad) + __ADD(DEVCONF_FORCE_TLLAO, force_tllao) +}; + +static char *inet6_devconf2str(int type, char *buf, size_t len) +{ + return __type2str(type, buf, len, inet6_devconf, + ARRAY_SIZE(inet6_devconf)); +} + + +static void inet6_dump_details(struct rtnl_link *link, + struct nl_dump_params *p, void *data) +{ + struct inet6_data *i6 = data; + char buf[64]; + int i, n = 0; + + nl_dump_line(p, " flags %s max-reasm-len %u timestamp %u " + "reachable-time %u retrans-time %u\n", + inet6_flags2str(i6->i6_flags, buf, sizeof(buf)), + i6->i6_cacheinfo.max_reasm_len, + i6->i6_cacheinfo.tstamp, + i6->i6_cacheinfo.reachable_time, + i6->i6_cacheinfo.retrans_time); + + nl_dump_line(p, " ipv6 devconf:\n"); + nl_dump_line(p, " "); + + for (i = 0; i < DEVCONF_MAX; i++) { + nl_dump_line(p, "%s %u", + inet6_devconf2str(i, buf, sizeof(buf)), i6->i6_conf[i]); + + if (++n == 3) { + nl_dump(p, "\n"); + nl_dump_line(p, " "); + n = 0; + } else + nl_dump(p, " "); + } + + if (n != 0) + nl_dump(p, "\n"); +} + +static void inet6_dump_stats(struct rtnl_link *link, + struct nl_dump_params *p, void *data) +{ + double octets; + char *octetsUnit; + + nl_dump(p, " IPv6: InPkts InOctets " + " InDiscards InDelivers\n"); + nl_dump(p, " %18llu ", link->l_stats[RTNL_LINK_INPKTS]); + + octets = nl_cancel_down_bytes(link->l_stats[RTNL_LINK_INOCTETS], + &octetsUnit); + if (octets) + nl_dump(p, "%14.2f %3s ", octets, octetsUnit); + else + nl_dump(p, "%16llu B ", 0); + + nl_dump(p, "%18llu %18llu\n", + link->l_stats[RTNL_LINK_INDISCARDS], + link->l_stats[RTNL_LINK_INDELIVERS]); + + nl_dump(p, " OutPkts OutOctets " + " OutDiscards OutForwards\n"); + + nl_dump(p, " %18llu ", link->l_stats[RTNL_LINK_OUTPKTS]); + + octets = nl_cancel_down_bytes(link->l_stats[RTNL_LINK_OUTOCTETS], + &octetsUnit); + if (octets) + nl_dump(p, "%14.2f %3s ", octets, octetsUnit); + else + nl_dump(p, "%16llu B ", 0); + + nl_dump(p, "%18llu %18llu\n", + link->l_stats[RTNL_LINK_OUTDISCARDS], + link->l_stats[RTNL_LINK_OUTFORWDATAGRAMS]); + + nl_dump(p, " InMcastPkts InMcastOctets " + " InBcastPkts InBcastOctests\n"); + + nl_dump(p, " %18llu ", link->l_stats[RTNL_LINK_INMCASTPKTS]); + + octets = nl_cancel_down_bytes(link->l_stats[RTNL_LINK_INMCASTOCTETS], + &octetsUnit); + if (octets) + nl_dump(p, "%14.2f %3s ", octets, octetsUnit); + else + nl_dump(p, "%16llu B ", 0); + + nl_dump(p, "%18llu ", link->l_stats[RTNL_LINK_INBCASTPKTS]); + octets = nl_cancel_down_bytes(link->l_stats[RTNL_LINK_INBCASTOCTETS], + &octetsUnit); + if (octets) + nl_dump(p, "%14.2f %3s\n", octets, octetsUnit); + else + nl_dump(p, "%16llu B\n", 0); + + nl_dump(p, " OutMcastPkts OutMcastOctets " + " OutBcastPkts OutBcastOctests\n"); + + nl_dump(p, " %18llu ", link->l_stats[RTNL_LINK_OUTMCASTPKTS]); + + octets = nl_cancel_down_bytes(link->l_stats[RTNL_LINK_OUTMCASTOCTETS], + &octetsUnit); + if (octets) + nl_dump(p, "%14.2f %3s ", octets, octetsUnit); + else + nl_dump(p, "%16llu B ", 0); + + nl_dump(p, "%18llu ", link->l_stats[RTNL_LINK_OUTBCASTPKTS]); + octets = nl_cancel_down_bytes(link->l_stats[RTNL_LINK_OUTBCASTOCTETS], + &octetsUnit); + if (octets) + nl_dump(p, "%14.2f %3s\n", octets, octetsUnit); + else + nl_dump(p, "%16llu B\n", 0); + + nl_dump(p, " ReasmOKs ReasmFails " + " ReasmReqds ReasmTimeout\n"); + nl_dump(p, " %18llu %18llu %18llu %18llu\n", + link->l_stats[RTNL_LINK_REASMOKS], + link->l_stats[RTNL_LINK_REASMFAILS], + link->l_stats[RTNL_LINK_REASMREQDS], + link->l_stats[RTNL_LINK_REASMTIMEOUT]); + + nl_dump(p, " FragOKs FragFails " + " FragCreates\n"); + nl_dump(p, " %18llu %18llu %18llu\n", + link->l_stats[RTNL_LINK_FRAGOKS], + link->l_stats[RTNL_LINK_FRAGFAILS], + link->l_stats[RTNL_LINK_FRAGCREATES]); + + nl_dump(p, " InHdrErrors InTooBigErrors " + " InNoRoutes InAddrErrors\n"); + nl_dump(p, " %18llu %18llu %18llu %18llu\n", + link->l_stats[RTNL_LINK_INHDRERRORS], + link->l_stats[RTNL_LINK_INTOOBIGERRORS], + link->l_stats[RTNL_LINK_INNOROUTES], + link->l_stats[RTNL_LINK_INADDRERRORS]); + + nl_dump(p, " InUnknownProtos InTruncatedPkts " + " OutNoRoutes\n"); + nl_dump(p, " %18llu %18llu %18llu\n", + link->l_stats[RTNL_LINK_INUNKNOWNPROTOS], + link->l_stats[RTNL_LINK_INTRUNCATEDPKTS], + link->l_stats[RTNL_LINK_OUTNOROUTES]); + + nl_dump(p, " ICMPv6: InMsgs InErrors " + " OutMsgs OutErrors\n"); + nl_dump(p, " %18llu %18llu %18llu %18llu\n", + link->l_stats[RTNL_LINK_ICMP6_INMSGS], + link->l_stats[RTNL_LINK_ICMP6_INERRORS], + link->l_stats[RTNL_LINK_ICMP6_OUTMSGS], + link->l_stats[RTNL_LINK_ICMP6_OUTERRORS]); +} + +static const struct nla_policy protinfo_policy = { + .type = NLA_NESTED, +}; + +static struct rtnl_link_af_ops inet6_ops = { + .ao_family = AF_INET6, + .ao_alloc = &inet6_alloc, + .ao_clone = &inet6_clone, + .ao_free = &inet6_free, + .ao_parse_protinfo = &inet6_parse_protinfo, + .ao_dump[NL_DUMP_DETAILS] = &inet6_dump_details, + .ao_dump[NL_DUMP_STATS] = &inet6_dump_stats, + .ao_protinfo_policy = &protinfo_policy, +}; + +static void __init inet6_init(void) +{ + rtnl_link_af_register(&inet6_ops); +} + +static void __exit inet6_exit(void) +{ + rtnl_link_af_unregister(&inet6_ops); +}