libnl/lib/route/link/inet6.c
Thomas Haller dfd0a80ec8 route: don't enforce minlen in inet6_parse_protinfo() (IFLA_PROTINFO) and inet_parse_af() (IFLA_AF_SPEC)
Older kernel version might have fewer values defined, so they would send
netlink messages that got rejected. Only check that at least one value
got sent.

This is especially grave as libnl uses an internal copy of the
kernel header files. Thus not only it is bound to the installed kernel
headers but to the libnl internal header copies that might easily be out
of sync with the kernel.

This affects IFLA_PROTINFO, inet6_parse_protinfo():
  - tb[IFLA_INET6_CONF], expecting DEVCONF_MAX
  - tb[IFLA_INET6_STATS], expecting __IPSTATS_MIB_MAX
  - tb[IFLA_INET6_ICMP6STATS], expecting __ICMP6_MIB_MAX
and IFLA_AF_SPEC, inet_parse_af():
  - tb[IFLA_INET_CONF], expecting IPV4_DEVCONF_MAX

https://bugzilla.redhat.com/show_bug.cgi?id=1062533

Acked-by: Thomas Graf <tgraf@suug.ch>
Signed-off-by: Thomas Haller <thaller@redhat.com>
2014-03-21 12:27:18 +01:00

385 lines
11 KiB
C

/*
* 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 <tgraf@suug.ch>
*/
#include <netlink-private/netlink.h>
#include <netlink/netlink.h>
#include <netlink/attr.h>
#include <netlink/route/rtnl.h>
#include <netlink-private/route/link/api.h>
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 = 4 },
[IFLA_INET6_STATS] = { .minlen = 8 },
[IFLA_INET6_ICMP6STATS] = { .minlen = 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_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));
/*
* 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 = min_t(int, __IPSTATS_MIB_MAX, nla_len(tb[IFLA_INET6_STATS]) / 8);
for (i = 1; i < len; 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;
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;
}
/* 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);
}