/* * 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)); /* * Due to 32bit data alignment, these addresses must be copied to an * aligned location prior to access. */ if (tb[IFLA_INET6_STATS]) { unsigned char *cnt = nla_data(tb[IFLA_INET6_STATS]); uint64_t stat; int i; for (i = 1; i < __IPSTATS_MIB_MAX; i++) { memcpy(&stat, &cnt[i * sizeof(stat)], sizeof(stat)); rtnl_link_set_stat(link, RTNL_LINK_IP6_INPKTS + i - 1, stat); } } if (tb[IFLA_INET6_ICMP6STATS]) { unsigned char *cnt = nla_data(tb[IFLA_INET6_ICMP6STATS]); uint64_t stat; int i; for (i = 1; i < __ICMP6_MIB_MAX; i++) { memcpy(&stat, &cnt[i * sizeof(stat)], sizeof(stat)); rtnl_link_set_stat(link, RTNL_LINK_ICMP6_INMSGS + i - 1, stat); } } 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 const 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 const 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], buf2[64]; int i, n = 0; nl_dump_line(p, " ipv6 max-reasm-len %s", nl_size2str(i6->i6_cacheinfo.max_reasm_len, buf, sizeof(buf))); nl_dump(p, " <%s>\n", inet6_flags2str(i6->i6_flags, buf, sizeof(buf))); nl_dump_line(p, " create-stamp %.2fs reachable-time %s", (double) i6->i6_cacheinfo.tstamp / 100., nl_msec2str(i6->i6_cacheinfo.reachable_time, buf, sizeof(buf))); nl_dump(p, " retrans-time %s\n", nl_msec2str(i6->i6_cacheinfo.retrans_time, buf, sizeof(buf))); nl_dump_line(p, " devconf:\n"); nl_dump_line(p, " "); for (i = 0; i < DEVCONF_MAX; i++) { uint32_t value = i6->i6_conf[i]; int x, offset; switch (i) { case DEVCONF_TEMP_VALID_LFT: case DEVCONF_TEMP_PREFERED_LFT: nl_msec2str((uint64_t) value * 1000., buf2, sizeof(buf2)); break; case DEVCONF_RTR_PROBE_INTERVAL: case DEVCONF_RTR_SOLICIT_INTERVAL: case DEVCONF_RTR_SOLICIT_DELAY: nl_msec2str(value, buf2, sizeof(buf2)); break; default: snprintf(buf2, sizeof(buf2), "%u", value); break; } inet6_devconf2str(i, buf, sizeof(buf)); offset = 23 - strlen(buf2); if (offset < 0) offset = 0; for (x = strlen(buf); x < offset; x++) buf[x] = ' '; strncpy(&buf[offset], buf2, strlen(buf2)); nl_dump_line(p, "%s", buf); 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, " %18" PRIu64 " ", link->l_stats[RTNL_LINK_IP6_INPKTS]); octets = nl_cancel_down_bytes(link->l_stats[RTNL_LINK_IP6_INOCTETS], &octetsUnit); if (octets) nl_dump(p, "%14.2f %3s ", octets, octetsUnit); else nl_dump(p, "%16" PRIu64 " B ", 0); nl_dump(p, "%18" PRIu64 " %18" PRIu64 "\n", link->l_stats[RTNL_LINK_IP6_INDISCARDS], link->l_stats[RTNL_LINK_IP6_INDELIVERS]); nl_dump(p, " OutPkts OutOctets " " OutDiscards OutForwards\n"); nl_dump(p, " %18" PRIu64 " ", link->l_stats[RTNL_LINK_IP6_OUTPKTS]); octets = nl_cancel_down_bytes(link->l_stats[RTNL_LINK_IP6_OUTOCTETS], &octetsUnit); if (octets) nl_dump(p, "%14.2f %3s ", octets, octetsUnit); else nl_dump(p, "%16" PRIu64 " B ", 0); nl_dump(p, "%18" PRIu64 " %18" PRIu64 "\n", link->l_stats[RTNL_LINK_IP6_OUTDISCARDS], link->l_stats[RTNL_LINK_IP6_OUTFORWDATAGRAMS]); nl_dump(p, " InMcastPkts InMcastOctets " " InBcastPkts InBcastOctests\n"); nl_dump(p, " %18" PRIu64 " ", link->l_stats[RTNL_LINK_IP6_INMCASTPKTS]); octets = nl_cancel_down_bytes(link->l_stats[RTNL_LINK_IP6_INMCASTOCTETS], &octetsUnit); if (octets) nl_dump(p, "%14.2f %3s ", octets, octetsUnit); else nl_dump(p, "%16" PRIu64 " B ", 0); nl_dump(p, "%18" PRIu64 " ", link->l_stats[RTNL_LINK_IP6_INBCASTPKTS]); octets = nl_cancel_down_bytes(link->l_stats[RTNL_LINK_IP6_INBCASTOCTETS], &octetsUnit); if (octets) nl_dump(p, "%14.2f %3s\n", octets, octetsUnit); else nl_dump(p, "%16" PRIu64 " B\n", 0); nl_dump(p, " OutMcastPkts OutMcastOctets " " OutBcastPkts OutBcastOctests\n"); nl_dump(p, " %18" PRIu64 " ", link->l_stats[RTNL_LINK_IP6_OUTMCASTPKTS]); octets = nl_cancel_down_bytes(link->l_stats[RTNL_LINK_IP6_OUTMCASTOCTETS], &octetsUnit); if (octets) nl_dump(p, "%14.2f %3s ", octets, octetsUnit); else nl_dump(p, "%16" PRIu64 " B ", 0); nl_dump(p, "%18" PRIu64 " ", link->l_stats[RTNL_LINK_IP6_OUTBCASTPKTS]); octets = nl_cancel_down_bytes(link->l_stats[RTNL_LINK_IP6_OUTBCASTOCTETS], &octetsUnit); if (octets) nl_dump(p, "%14.2f %3s\n", octets, octetsUnit); else nl_dump(p, "%16" PRIu64 " B\n", 0); nl_dump(p, " ReasmOKs ReasmFails " " ReasmReqds ReasmTimeout\n"); nl_dump(p, " %18" PRIu64 " %18" PRIu64 " %18" PRIu64 " %18" PRIu64 "\n", link->l_stats[RTNL_LINK_IP6_REASMOKS], link->l_stats[RTNL_LINK_IP6_REASMFAILS], link->l_stats[RTNL_LINK_IP6_REASMREQDS], link->l_stats[RTNL_LINK_IP6_REASMTIMEOUT]); nl_dump(p, " FragOKs FragFails " " FragCreates\n"); nl_dump(p, " %18" PRIu64 " %18" PRIu64 " %18" PRIu64 "\n", link->l_stats[RTNL_LINK_IP6_FRAGOKS], link->l_stats[RTNL_LINK_IP6_FRAGFAILS], link->l_stats[RTNL_LINK_IP6_FRAGCREATES]); nl_dump(p, " InHdrErrors InTooBigErrors " " InNoRoutes InAddrErrors\n"); nl_dump(p, " %18" PRIu64 " %18" PRIu64 " %18" PRIu64 " %18" PRIu64 "\n", link->l_stats[RTNL_LINK_IP6_INHDRERRORS], link->l_stats[RTNL_LINK_IP6_INTOOBIGERRORS], link->l_stats[RTNL_LINK_IP6_INNOROUTES], link->l_stats[RTNL_LINK_IP6_INADDRERRORS]); nl_dump(p, " InUnknownProtos InTruncatedPkts " " OutNoRoutes\n"); nl_dump(p, " %18" PRIu64 " %18" PRIu64 " %18" PRIu64 "\n", link->l_stats[RTNL_LINK_IP6_INUNKNOWNPROTOS], link->l_stats[RTNL_LINK_IP6_INTRUNCATEDPKTS], link->l_stats[RTNL_LINK_IP6_OUTNOROUTES]); nl_dump(p, " ICMPv6: InMsgs InErrors " " OutMsgs OutErrors\n"); nl_dump(p, " %18" PRIu64 " %18" PRIu64 " %18" PRIu64 " %18" PRIu64 "\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_parse_af = &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); }