} #endif /* Conntrack netlink parsing. */ static bool nl_ct_parse_counters(struct nlattr *nla, struct ct_dpif_counters *counters) { static const struct nl_policy policy[] = { [CTA_COUNTERS_PACKETS] = { .type = NL_A_BE64, .optional = false }, [CTA_COUNTERS_BYTES] = { .type = NL_A_BE64, .optional = false }, }; struct nlattr *attrs[ARRAY_SIZE(policy)]; bool parsed; parsed = nl_parse_nested(nla, policy, attrs, ARRAY_SIZE(policy)); if (parsed) { counters->packets = ntohll(nl_attr_get_be64(attrs[CTA_COUNTERS_PACKETS])); counters->bytes = ntohll(nl_attr_get_be64(attrs[CTA_COUNTERS_BYTES])); } else { VLOG_ERR_RL(&rl, "Could not parse nested counters. " "Possibly incompatible Linux kernel version."); } return parsed; } static bool nl_ct_parse_timestamp(struct nlattr *nla, struct ct_dpif_timestamp *timestamp)
static int route_msg_parser(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) { struct nl_parser_param *pp = arg; struct rtnl_route route = RTNL_INIT_ROUTE(); struct rtattr *tb[RTA_MAX + 1]; struct rtmsg *r = NLMSG_DATA(n); size_t len; int err; if (n->nlmsg_type != RTM_NEWROUTE) return P_IGNORE; len = n->nlmsg_len - NLMSG_LENGTH(sizeof(*r)); if (len < 0) return nl_error(EINVAL, "netlink message too short to be a " \ "routing message"); err = nl_parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len); if (err < 0) return err; route.rt_family = r->rtm_family; route.rt_dst_len = r->rtm_dst_len; route.rt_src_len = r->rtm_src_len; route.rt_tos = r->rtm_tos; route.rt_table = r->rtm_table; route.rt_protocol = r->rtm_protocol; route.rt_scope = r->rtm_scope; route.rt_type = r->rtm_type; route.rt_flags = r->rtm_flags; route.rt_mask = (ROUTE_HAS_FAMILY|ROUTE_HAS_DST_LEN|ROUTE_HAS_SRC_LEN| ROUTE_HAS_TOS|ROUTE_HAS_TABLE|ROUTE_HAS_PROTOCOL|ROUTE_HAS_SCOPE| ROUTE_HAS_TYPE|ROUTE_HAS_FLAGS); if (tb[RTA_DST]) { nl_copy_addr(&route.rt_dst, tb[RTA_DST]); route.rt_dst.a_prefix = route.rt_dst_len; route.rt_dst.a_family = route.rt_family; route.rt_mask |= ROUTE_HAS_DST; } if (tb[RTA_SRC]) { nl_copy_addr(&route.rt_src, tb[RTA_SRC]); route.rt_src.a_prefix = route.rt_src_len; route.rt_src.a_family = route.rt_family; route.rt_mask |= ROUTE_HAS_SRC; } if (tb[RTA_IIF]) { err = NL_COPY_DATA(route.rt_iif, tb[RTA_IIF]); if (err < 0) return err; route.rt_mask |= ROUTE_HAS_IIF; } if (tb[RTA_OIF]) { err = NL_COPY_DATA(route.rt_oif, tb[RTA_OIF]); if (err < 0) return err; route.rt_mask |= ROUTE_HAS_OIF; } if (tb[RTA_GATEWAY]) { nl_copy_addr(&route.rt_gateway, tb[RTA_GATEWAY]); route.rt_gateway.a_family = route.rt_family; route.rt_mask |= ROUTE_HAS_GATEWAY; } if (tb[RTA_PRIORITY]) { err = NL_COPY_DATA(route.rt_prio, tb[RTA_PRIORITY]); if (err < 0) return err; route.rt_mask |= ROUTE_HAS_PRIO; } if (tb[RTA_PREFSRC]) { nl_copy_addr(&route.rt_pref_src, tb[RTA_PREFSRC]); route.rt_pref_src.a_family = route.rt_family; route.rt_mask |= ROUTE_HAS_PREF_SRC; } if (tb[RTA_METRICS]) { int i; struct rtattr *mtb[RTAX_MAX + 1]; err = nl_parse_nested(mtb, RTAX_MAX, tb[RTA_METRICS]); if (err < 0) return err; for (i = 1; i <= RTAX_MAX; i++) { if (mtb[i]) { uint32_t m = *(uint32_t *) RTA_DATA(mtb[i]); route.rt_metrics[i] = m; route.rt_metrics_mask |= (1<<(i-1)); } } route.rt_mask |= ROUTE_HAS_METRICS; } if (tb[RTA_MULTIPATH]) { struct rtnl_nexthop *nh; struct rtnexthop *rtnh = RTA_DATA(tb[RTA_MULTIPATH]); size_t tlen = RTA_PAYLOAD(tb[RTA_MULTIPATH]); for (;;) { if (tlen < sizeof(*rtnh) || tlen < rtnh->rtnh_len) break; nh = calloc(1, sizeof(*nh)); if (nh == NULL) { err = -ENOMEM; goto err_out; } nh->rtnh_flags = rtnh->rtnh_flags; nh->rtnh_hops = rtnh->rtnh_hops; nh->rtnh_ifindex = rtnh->rtnh_ifindex; nh->rtnh_mask = (NEXTHOP_HAS_FLAGS | NEXTHOP_HAS_HOPS | NEXTHOP_HAS_IFINDEX); if (rtnh->rtnh_len > sizeof(*rtnh)) { struct rtattr *ntb[RTA_MAX + 1]; nl_parse_rtattr(ntb, RTA_MAX, RTNH_DATA(rtnh), rtnh->rtnh_len - sizeof(*rtnh)); if (ntb[RTA_GATEWAY]) { nl_copy_addr(&nh->rtnh_gateway, ntb[RTA_GATEWAY]); nh->rtnh_gateway.a_family = route.rt_family; nh->rtnh_mask = NEXTHOP_HAS_GATEWAY; } } nh->rtnh_next = route.rt_nexthops; route.rt_nexthops = nh; len -= RTNH_ALIGN(rtnh->rtnh_len); rtnh = RTNH_NEXT(rtnh); } route.rt_mask |= ROUTE_HAS_MULTIPATH; } /* Not sure if there are any users not using this for fwmark, * allocating for now */ if (tb[RTA_PROTOINFO]) { err = nl_alloc_data_from_rtattr(&route.rt_protoinfo, tb[RTA_PROTOINFO]); if (err < 0) goto err_out; route.rt_mask |= ROUTE_HAS_PROTOINFO; } if (tb[RTA_FLOW]) { err = NL_COPY_DATA(route.rt_realm, tb[RTA_FLOW]); if (err < 0) goto err_out; route.rt_mask |= ROUTE_HAS_REALM; } if (tb[RTA_CACHEINFO]) { struct rta_cacheinfo *ci; if (RTA_PAYLOAD(tb[RTA_CACHEINFO]) < sizeof(struct rta_cacheinfo)) return nl_error(EINVAL, "routing cacheinfo TLV is too short"); ci = (struct rta_cacheinfo *) RTA_DATA(tb[RTA_CACHEINFO]); route.rt_cacheinfo.rtci_clntref = ci->rta_clntref; route.rt_cacheinfo.rtci_last_use = ci->rta_lastuse; route.rt_cacheinfo.rtci_expires = ci->rta_expires; route.rt_cacheinfo.rtci_error = ci->rta_error; route.rt_cacheinfo.rtci_used = ci->rta_used; route.rt_cacheinfo.rtci_id = ci->rta_id; route.rt_cacheinfo.rtci_ts = ci->rta_ts; route.rt_cacheinfo.rtci_tsage = ci->rta_tsage; route.rt_mask |= ROUTE_HAS_CACHEINFO; } if (tb[RTA_SESSION]) { err = nl_alloc_data_from_rtattr(&route.rt_session, tb[RTA_SESSION]); if (err < 0) goto err_out; route.rt_mask |= ROUTE_HAS_SESSION; } err = pp->pp_cb((struct nl_common *) &route, pp); if (err < 0) goto err_out; return P_ACCEPT; err_out: route_free_data((struct nl_common *) &route); return err; }