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; }
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_CONF] && nla_len(tb[IFLA_INET6_CONF]) % 4) return -EINVAL; if (tb[IFLA_INET6_STATS] && nla_len(tb[IFLA_INET6_STATS]) % 8) return -EINVAL; if (tb[IFLA_INET6_ICMP6STATS] && nla_len(tb[IFLA_INET6_ICMP6STATS]) % 8) return -EINVAL; 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_TOKEN]) nla_memcpy(&i6->i6_token, tb[IFLA_INET6_TOKEN], sizeof(struct in6_addr)); if (tb[IFLA_INET6_ADDR_GEN_MODE]) i6->i6_addr_gen_mode = nla_get_u8 (tb[IFLA_INET6_ADDR_GEN_MODE]); /* * 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; int len = nla_len(tb[IFLA_INET6_STATS]) / 8; const uint8_t *map_stat_id = map_stat_id_from_IPSTATS_MIB_v2; if (len < 32 || (tb[IFLA_INET6_ICMP6STATS] && nla_len(tb[IFLA_INET6_ICMP6STATS]) < 6)) { /* kernel commit 14a196807482e6fc74f15fc03176d5c08880588f reordered the values. * The later commit 6a5dc9e598fe90160fee7de098fa319665f5253e added values * IPSTATS_MIB_CSUMERRORS/ICMP6_MIB_CSUMERRORS. If the netlink is shorter * then this, assume that the kernel uses the previous meaning of the * enumeration. */ map_stat_id = map_stat_id_from_IPSTATS_MIB_v1; } len = min_t(int, __IPSTATS_MIB_MAX, len); for (i = 1; i < len; i++) { memcpy(&stat, &cnt[i * sizeof(stat)], sizeof(stat)); rtnl_link_set_stat(link, map_stat_id[i], stat); } } if (tb[IFLA_INET6_ICMP6STATS]) { unsigned char *cnt = nla_data(tb[IFLA_INET6_ICMP6STATS]); uint64_t stat; int i; int len = min_t(int, __ICMP6_MIB_MAX, nla_len(tb[IFLA_INET6_ICMP6STATS]) / 8); for (i = 1; i < len; i++) { memcpy(&stat, &cnt[i * sizeof(stat)], sizeof(stat)); rtnl_link_set_stat(link, RTNL_LINK_ICMP6_INMSGS + i - 1, stat); } } return 0; }