diff --git a/Makefile.am b/Makefile.am index 1b95a5593..aaf44c8a4 100644 --- a/Makefile.am +++ b/Makefile.am @@ -100,6 +100,7 @@ libnlinclude_netlink_route_HEADERS = \ include/netlink/route/link.h \ include/netlink/route/neighbour.h \ include/netlink/route/neightbl.h \ + include/netlink/route/netconf.h \ include/netlink/route/nexthop.h \ include/netlink/route/pktloc.h \ include/netlink/route/qdisc.h \ @@ -380,6 +381,7 @@ lib_libnl_route_3_la_SOURCES = \ lib/route/link/vxlan.c \ lib/route/neigh.c \ lib/route/neightbl.c \ + lib/route/netconf.c \ lib/route/nexthop.c \ lib/route/pktloc.c \ lib/route/qdisc/blackhole.c \ diff --git a/include/linux-private/linux/netconf.h b/include/linux-private/linux/netconf.h new file mode 100644 index 000000000..76ecfcc2e --- /dev/null +++ b/include/linux-private/linux/netconf.h @@ -0,0 +1,28 @@ +#ifndef __LINUX_NETCONF_H_ +#define __LINUX_NETCONF_H_ + +#include +#include + +struct netconfmsg { + __u8 ncm_family; +}; + +enum { + NETCONFA_UNSPEC, + NETCONFA_IFINDEX, + NETCONFA_FORWARDING, + NETCONFA_RP_FILTER, + NETCONFA_MC_FORWARDING, + NETCONFA_PROXY_NEIGH, + NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN, + NETCONFA_INPUT, + __NETCONFA_MAX +}; +#define NETCONFA_MAX (__NETCONFA_MAX - 1) +#define NETCONFA_ALL -1 + +#define NETCONFA_IFINDEX_ALL -1 +#define NETCONFA_IFINDEX_DEFAULT -2 + +#endif /* __LINUX_NETCONF_H_ */ diff --git a/include/linux-private/linux/rtnetlink.h b/include/linux-private/linux/rtnetlink.h index 32449478c..a92c7c41c 100644 --- a/include/linux-private/linux/rtnetlink.h +++ b/include/linux-private/linux/rtnetlink.h @@ -122,6 +122,8 @@ enum { RTM_NEWNETCONF = 80, #define RTM_NEWNETCONF RTM_NEWNETCONF + RTM_DELNETCONF, +#define RTM_DELNETCONF RTM_DELNETCONF RTM_GETNETCONF = 82, #define RTM_GETNETCONF RTM_GETNETCONF @@ -645,6 +647,8 @@ enum rtnetlink_groups { #define RTNLGRP_MPLS_ROUTE RTNLGRP_MPLS_ROUTE RTNLGRP_NSID, #define RTNLGRP_NSID RTNLGRP_NSID + RTNLGRP_MPLS_NETCONF, +#define RTNLGRP_MPLS_NETCONF RTNLGRP_MPLS_NETCONF __RTNLGRP_MAX }; #define RTNLGRP_MAX (__RTNLGRP_MAX - 1) diff --git a/include/netlink/netlink-compat.h b/include/netlink/netlink-compat.h index 17ec9fc8d..2839ed034 100644 --- a/include/netlink/netlink-compat.h +++ b/include/netlink/netlink-compat.h @@ -47,4 +47,8 @@ typedef unsigned short sa_family_t; #define AF_LLC 26 #endif +#ifndef AF_MPLS +#define AF_MPLS 28 +#endif + #endif diff --git a/include/netlink/route/netconf.h b/include/netlink/route/netconf.h new file mode 100644 index 000000000..199343812 --- /dev/null +++ b/include/netlink/route/netconf.h @@ -0,0 +1,44 @@ +/* + * netlink/route/netconf.h rtnetlink netconf layer + * + * 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) 2017 David Ahern + */ + +#ifndef NETCONF_H_ +#define NETCONF_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +struct rtnl_netconf; + +int rtnl_netconf_alloc_cache(struct nl_sock *sk, struct nl_cache **result); + +struct rtnl_netconf *rtnl_netconf_get_by_idx(struct nl_cache *cache, int family, + int ifindex); +struct rtnl_netconf *rtnl_netconf_get_all(struct nl_cache *cache, + int family); +struct rtnl_netconf *rtnl_netconf_get_default(struct nl_cache *cache, + int family); +void rtnl_netconf_put(struct rtnl_netconf *nc); + +int rtnl_netconf_get_family(struct rtnl_netconf *nc, int *val); +int rtnl_netconf_get_ifindex(struct rtnl_netconf *nc, int *val); +int rtnl_netconf_get_forwarding(struct rtnl_netconf *nc, int *val); +int rtnl_netconf_get_mc_forwarding(struct rtnl_netconf *nc, int *val); +int rtnl_netconf_get_rp_filter(struct rtnl_netconf *nc, int *val); +int rtnl_netconf_get_proxy_neigh(struct rtnl_netconf *nc, int *val); +int rtnl_netconf_get_ignore_routes_linkdown(struct rtnl_netconf *nc, int *val); +int rtnl_netconf_get_input(struct rtnl_netconf *nc, int *val); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/route/netconf.c b/lib/route/netconf.c new file mode 100644 index 000000000..0635dd7f2 --- /dev/null +++ b/lib/route/netconf.c @@ -0,0 +1,580 @@ +/* + * lib/route/netconf.c netconf + * + * 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) 2017 David Ahern + */ + +/** + * @ingroup rtnl + * @defgroup netconf Netconf + * @brief + * + * @{ + */ + +#include +#include +#include +#include +#include +#include +#include + +/** @cond SKIP */ +#define NETCONF_ATTR_FAMILY 0x0001 +#define NETCONF_ATTR_IFINDEX 0x0002 +#define NETCONF_ATTR_RP_FILTER 0x0004 +#define NETCONF_ATTR_FWDING 0x0008 +#define NETCONF_ATTR_MC_FWDING 0x0010 +#define NETCONF_ATTR_PROXY_NEIGH 0x0020 +#define NETCONF_ATTR_IGNORE_RT_LINKDWN 0x0040 +#define NETCONF_ATTR_INPUT 0x0080 + +struct rtnl_netconf +{ + NLHDR_COMMON + + int family; + int ifindex; + int rp_filter; + int forwarding; + int mc_forwarding; + int proxy_neigh; + int ignore_routes_linkdown; + int input; +}; + +static struct nl_cache_ops rtnl_netconf_ops; +static struct nl_object_ops netconf_obj_ops; +/** @endcond */ + +static struct nla_policy devconf_ipv4_policy[NETCONFA_MAX+1] = { + [NETCONFA_IFINDEX] = { .type = NLA_S32 }, + [NETCONFA_FORWARDING] = { .type = NLA_S32 }, + [NETCONFA_MC_FORWARDING] = { .type = NLA_S32 }, + [NETCONFA_RP_FILTER] = { .type = NLA_S32 }, + [NETCONFA_PROXY_NEIGH] = { .type = NLA_S32 }, + [NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN] = { .type = NLA_S32 }, +}; + +static struct nla_policy devconf_ipv6_policy[NETCONFA_MAX+1] = { + [NETCONFA_IFINDEX] = { .type = NLA_S32 }, + [NETCONFA_FORWARDING] = { .type = NLA_S32 }, + [NETCONFA_MC_FORWARDING] = { .type = NLA_S32 }, + [NETCONFA_PROXY_NEIGH] = { .type = NLA_S32 }, + [NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN] = { .type = NLA_S32 }, +}; + +static struct nla_policy devconf_mpls_policy[NETCONFA_MAX+1] = { + [NETCONFA_IFINDEX] = { .type = NLA_S32 }, + [NETCONFA_INPUT] = { .type = NLA_S32 }, +}; + +static struct rtnl_netconf *rtnl_netconf_alloc(void) +{ + return (struct rtnl_netconf *) nl_object_alloc(&netconf_obj_ops); +} + +static int netconf_clone(struct nl_object *_dst, struct nl_object *_src) +{ + struct rtnl_netconf *dst = nl_object_priv(_dst); + struct rtnl_netconf *src = nl_object_priv(_src); + + *dst = *src; + + return 0; +} + +static int netconf_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, + struct nlmsghdr *nlh, struct nl_parser_param *pp) +{ + struct nlattr *tb[NETCONFA_MAX+1], *attr; + struct rtnl_netconf *nc; + struct netconfmsg *ncm; + int err; + + ncm = nlmsg_data(nlh); + switch (ncm->ncm_family) { + case AF_INET: + err = nlmsg_parse(nlh, sizeof(*ncm), tb, NETCONFA_MAX, + devconf_ipv4_policy); + if (err < 0) + return err; + break; + case AF_INET6: + err = nlmsg_parse(nlh, sizeof(*ncm), tb, NETCONFA_MAX, + devconf_ipv6_policy); + if (err < 0) + return err; + break; + case AF_MPLS: + err = nlmsg_parse(nlh, sizeof(*ncm), tb, NETCONFA_MAX, + devconf_mpls_policy); + if (err < 0) + return err; + break; + default: + printf("unexpected netconf family: %d\n", ncm->ncm_family); + return -1; + } + + if (!tb[NETCONFA_IFINDEX]) + return -1; + + nc = rtnl_netconf_alloc(); + if (!nc) + return -NLE_NOMEM; + + nc->ce_msgtype = nlh->nlmsg_type; + nc->family = ncm->ncm_family; + nc->ifindex = nla_get_s32(tb[NETCONFA_IFINDEX]); + + nc->ce_mask = NETCONF_ATTR_FAMILY | NETCONF_ATTR_IFINDEX; + + + if (tb[NETCONFA_RP_FILTER]) { + attr = tb[NETCONFA_RP_FILTER]; + nc->rp_filter = nla_get_s32(attr); + nc->ce_mask |= NETCONF_ATTR_RP_FILTER; + } + + if (tb[NETCONFA_FORWARDING]) { + attr = tb[NETCONFA_FORWARDING]; + nc->forwarding = nla_get_s32(attr); + nc->ce_mask |= NETCONF_ATTR_FWDING; + } + + if (tb[NETCONFA_MC_FORWARDING]) { + attr = tb[NETCONFA_MC_FORWARDING]; + nc->mc_forwarding = nla_get_s32(attr); + nc->ce_mask |= NETCONF_ATTR_MC_FWDING; + } + + if (tb[NETCONFA_PROXY_NEIGH]) { + attr = tb[NETCONFA_PROXY_NEIGH]; + nc->proxy_neigh = nla_get_s32(attr); + nc->ce_mask |= NETCONF_ATTR_PROXY_NEIGH; + } + + if (tb[NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN]) { + attr = tb[NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN]; + nc->ignore_routes_linkdown = nla_get_s32(attr); + nc->ce_mask |= NETCONF_ATTR_IGNORE_RT_LINKDWN; + } + + if (tb[NETCONFA_INPUT]) { + attr = tb[NETCONFA_INPUT]; + nc->input = nla_get_s32(attr); + nc->ce_mask |= NETCONF_ATTR_INPUT; + } + + return pp->pp_cb((struct nl_object *) nc, pp); +} + +static int netconf_request_update(struct nl_cache *cache, struct nl_sock *sk) +{ + struct netconfmsg nc = { + .ncm_family = cache->c_iarg1, + }; + + return nl_send_simple(sk, RTM_GETNETCONF, NLM_F_DUMP, &nc, sizeof(nc)); +} + +static void netconf_dump_line(struct nl_object *obj, struct nl_dump_params *p) +{ + struct rtnl_netconf *nc = (struct rtnl_netconf *) obj; + struct nl_cache *link_cache; + char buf[64]; + + switch(nc->family) { + case AF_INET: + nl_dump(p, "ipv4 "); + break; + case AF_INET6: + nl_dump(p, "ipv6 "); + break; + case AF_MPLS: + nl_dump(p, "mpls "); + break; + default: + return; + } + + switch(nc->ifindex) { + case NETCONFA_IFINDEX_ALL: + nl_dump(p, "all "); + break; + case NETCONFA_IFINDEX_DEFAULT: + nl_dump(p, "default "); + break; + default: + link_cache = nl_cache_mngt_require_safe("route/link"); + if (link_cache) { + nl_dump(p, "dev %s ", + rtnl_link_i2name(link_cache, nc->ifindex, + buf, sizeof(buf))); + nl_cache_put(link_cache); + } else + nl_dump(p, "dev %d ", nc->ifindex); + } + + if (nc->ce_mask & NETCONF_ATTR_FWDING) { + nl_dump(p, "forwarding %s ", + nc->forwarding ? "on" : "off"); + } + + if (nc->ce_mask & NETCONF_ATTR_RP_FILTER) { + if (nc->rp_filter == 0) + nl_dump(p, "rp_filter off "); + else if (nc->rp_filter == 1) + nl_dump(p, "rp_filter strict "); + else if (nc->rp_filter == 2) + nl_dump(p, "rp_filter loose "); + else + nl_dump(p, "rp_filter unknown-mode "); + } + + if (nc->ce_mask & NETCONF_ATTR_MC_FWDING) { + nl_dump(p, "mc_forwarding %s ", + nc->mc_forwarding ? "on" : "off"); + } + + if (nc->ce_mask & NETCONF_ATTR_PROXY_NEIGH) + nl_dump(p, "proxy_neigh %d ", nc->proxy_neigh); + + if (nc->ce_mask & NETCONF_ATTR_IGNORE_RT_LINKDWN) { + nl_dump(p, "ignore_routes_with_linkdown %s ", + nc->ignore_routes_linkdown ? "on" : "off"); + } + + if (nc->ce_mask & NETCONF_ATTR_INPUT) + nl_dump(p, "input %s ", nc->input ? "on" : "off"); + + nl_dump(p, "\n"); +} + +static const struct trans_tbl netconf_attrs[] = { + __ADD(NETCONF_ATTR_FAMILY, family), + __ADD(NETCONF_ATTR_IFINDEX, ifindex), + __ADD(NETCONF_ATTR_RP_FILTER, rp_filter), + __ADD(NETCONF_ATTR_FWDING, forwarding), + __ADD(NETCONF_ATTR_MC_FWDING, mc_forwarding), + __ADD(NETCONF_ATTR_PROXY_NEIGH, proxy_neigh), + __ADD(NETCONF_ATTR_IGNORE_RT_LINKDWN, ignore_routes_with_linkdown), + __ADD(NETCONF_ATTR_INPUT, input), +}; + +static char *netconf_attrs2str(int attrs, char *buf, size_t len) +{ + return __flags2str(attrs, buf, len, netconf_attrs, + ARRAY_SIZE(netconf_attrs)); +} + +static void netconf_keygen(struct nl_object *obj, uint32_t *hashkey, + uint32_t table_sz) +{ + struct rtnl_netconf *nc = (struct rtnl_netconf *) obj; + unsigned int nckey_sz; + struct nc_hash_key { + int nc_family; + int nc_index; + } __attribute__((packed)) nckey; + + nckey_sz = sizeof(nckey); + nckey.nc_family = nc->family; + nckey.nc_index = nc->ifindex; + + *hashkey = nl_hash(&nckey, nckey_sz, 0) % table_sz; + + NL_DBG(5, "netconf %p key (dev %d fam %d) keysz %d, hash 0x%x\n", + nc, nckey.nc_index, nckey.nc_family, nckey_sz, *hashkey); +} + +static uint64_t netconf_compare(struct nl_object *_a, struct nl_object *_b, + uint64_t attrs, int flags) +{ + struct rtnl_netconf *a = (struct rtnl_netconf *) _a; + struct rtnl_netconf *b = (struct rtnl_netconf *) _b; + uint64_t diff = 0; + +#define NETCONF_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, NETCONF_ATTR_##ATTR, a, b, EXPR) + + diff |= NETCONF_DIFF(FAMILY, a->family != b->family); + diff |= NETCONF_DIFF(IFINDEX, a->ifindex != b->ifindex); + diff |= NETCONF_DIFF(RP_FILTER, a->rp_filter != b->rp_filter); + diff |= NETCONF_DIFF(FWDING, a->forwarding != b->forwarding); + diff |= NETCONF_DIFF(MC_FWDING, a->mc_forwarding != b->mc_forwarding); + diff |= NETCONF_DIFF(PROXY_NEIGH, a->proxy_neigh != b->proxy_neigh); + diff |= NETCONF_DIFF(IGNORE_RT_LINKDWN, + a->ignore_routes_linkdown != b->ignore_routes_linkdown); + diff |= NETCONF_DIFF(INPUT, a->input != b->input); + +#undef NETCONF_DIFF + + return diff; +} + +static int netconf_update(struct nl_object *old_obj, struct nl_object *new_obj) +{ + struct rtnl_netconf *new_nc = (struct rtnl_netconf *) new_obj; + struct rtnl_netconf *old_nc = (struct rtnl_netconf *) old_obj; + int action = new_obj->ce_msgtype; + + switch(action) { + case RTM_NEWNETCONF: + if (new_nc->family != old_nc->family || + new_nc->ifindex != old_nc->ifindex) + return -NLE_OPNOTSUPP; + + if (new_nc->ce_mask & NETCONF_ATTR_RP_FILTER) + old_nc->rp_filter = new_nc->rp_filter; + if (new_nc->ce_mask & NETCONF_ATTR_FWDING) + old_nc->forwarding = new_nc->forwarding; + if (new_nc->ce_mask & NETCONF_ATTR_MC_FWDING) + old_nc->mc_forwarding = new_nc->mc_forwarding; + if (new_nc->ce_mask & NETCONF_ATTR_PROXY_NEIGH) + old_nc->proxy_neigh = new_nc->proxy_neigh; + if (new_nc->ce_mask & NETCONF_ATTR_IGNORE_RT_LINKDWN) + old_nc->ignore_routes_linkdown = new_nc->ignore_routes_linkdown; + + break; + default: + return -NLE_OPNOTSUPP; + } + + return NLE_SUCCESS; +} + +/** + * @name Cache Management + * @{ + */ + +int rtnl_netconf_alloc_cache(struct nl_sock *sk, struct nl_cache **result) +{ + return nl_cache_alloc_and_fill(&rtnl_netconf_ops, sk, result); +} + +/** + * Search netconf in cache + * @arg cache netconf cache + * @arg family Address family of interest + * @arg ifindex Interface index of interest + * + * Searches netconf cache previously allocated with rtnl_netconf_alloc_cache() + * for given index and family + * + * The reference counter is incremented before returning the netconf entry, + * therefore the reference must be given back with rtnl_netconf_put() after + * usage. + * + * @return netconf object or NULL if no match was found. + */ +struct rtnl_netconf *rtnl_netconf_get_by_idx(struct nl_cache *cache, int family, + int ifindex) +{ + struct rtnl_netconf *nc; + + if (!ifindex || !family || cache->c_ops != &rtnl_netconf_ops) + return NULL; + + nl_list_for_each_entry(nc, &cache->c_items, ce_list) { + if (nc->ifindex == ifindex && + nc->family == family) { + nl_object_get((struct nl_object *) nc); + return nc; + } + } + + return NULL; +} + +void rtnl_netconf_put(struct rtnl_netconf *nc) +{ + nl_object_put((struct nl_object *) nc); +} + +/** + * Search netconf in cache + * @arg cache netconf cache + * @arg family Address family of interest + * + * Searches netconf cache previously allocated with rtnl_netconf_alloc_cache() + * for "all" netconf settings for given family + * + * The reference counter is incremented before returning the netconf entry, + * therefore the reference must be given back with rtnl_netconf_put() after + * usage. + * + * @return netconf object or NULL if no match was found. + */ +struct rtnl_netconf *rtnl_netconf_get_all(struct nl_cache *cache, int family) +{ + return rtnl_netconf_get_by_idx(cache, family, NETCONFA_IFINDEX_ALL); +} + +/** + * Search netconf in cache + * @arg cache netconf cache + * @arg family Address family of interest + * + * Searches netconf cache previously allocated with rtnl_netconf_alloc_cache() + * for "default" netconf settings for given family + * + * The reference counter is incremented before returning the netconf entry, + * therefore the reference must be given back with rtnl_netconf_put() after + * usage. + * + * @return netconf object or NULL if no match was found. + */ +struct rtnl_netconf *rtnl_netconf_get_default(struct nl_cache *cache, int family) +{ + return rtnl_netconf_get_by_idx(cache, family, NETCONFA_IFINDEX_DEFAULT); +} + +/** @} */ + +/** + * @name Attributes + * @{ + */ + +int rtnl_netconf_get_family(struct rtnl_netconf *nc, int *val) +{ + if (!nc) + return -NLE_INVAL; + if (!(nc->ce_mask & NETCONF_ATTR_FAMILY)) + return -NLE_MISSING_ATTR; + if (val) + *val = nc->family; + return 0; +} +int rtnl_netconf_get_ifindex(struct rtnl_netconf *nc, int *val) +{ + if (!nc) + return -NLE_INVAL; + if (!(nc->ce_mask & NETCONF_ATTR_IFINDEX)) + return -NLE_MISSING_ATTR; + if (val) + *val = nc->ifindex; + return 0; +} +int rtnl_netconf_get_forwarding(struct rtnl_netconf *nc, int *val) +{ + if (!nc) + return -NLE_INVAL; + if (!(nc->ce_mask & NETCONF_ATTR_FWDING)) + return -NLE_MISSING_ATTR; + if (val) + *val = nc->forwarding; + return 0; +} +int rtnl_netconf_get_mc_forwarding(struct rtnl_netconf *nc, int *val) +{ + if (!nc) + return -NLE_INVAL; + if (!(nc->ce_mask & NETCONF_ATTR_MC_FWDING)) + return -NLE_MISSING_ATTR; + if (val) + *val = nc->mc_forwarding; + return 0; +} +int rtnl_netconf_get_rp_filter(struct rtnl_netconf *nc, int *val) +{ + if (!nc) + return -NLE_INVAL; + if (!(nc->ce_mask & NETCONF_ATTR_RP_FILTER)) + return -NLE_MISSING_ATTR; + if (val) + *val = nc->rp_filter; + return 0; +} +int rtnl_netconf_get_proxy_neigh(struct rtnl_netconf *nc, int *val) +{ + if (!nc) + return -NLE_INVAL; + if (!(nc->ce_mask & NETCONF_ATTR_PROXY_NEIGH)) + return -NLE_MISSING_ATTR; + if (val) + *val = nc->proxy_neigh; + return 0; +} +int rtnl_netconf_get_ignore_routes_linkdown(struct rtnl_netconf *nc, int *val) +{ + if (!nc) + return -NLE_INVAL; + if (!(nc->ce_mask & NETCONF_ATTR_IGNORE_RT_LINKDWN)) + return -NLE_MISSING_ATTR; + if (val) + *val = nc->ignore_routes_linkdown; + return 0; +} +int rtnl_netconf_get_input(struct rtnl_netconf *nc, int *val) +{ + if (!nc) + return -NLE_INVAL; + if (!(nc->ce_mask & NETCONF_ATTR_INPUT)) + return -NLE_MISSING_ATTR; + if (val) + *val = nc->input; + return 0; +} + + +/** @} */ + +static struct nl_object_ops netconf_obj_ops = { + .oo_name = "route/netconf", + .oo_size = sizeof(struct rtnl_netconf), + .oo_clone = netconf_clone, + .oo_dump = { + [NL_DUMP_LINE] = netconf_dump_line, + [NL_DUMP_DETAILS] = netconf_dump_line, + }, + .oo_compare = netconf_compare, + .oo_keygen = netconf_keygen, + .oo_update = netconf_update, + .oo_attrs2str = netconf_attrs2str, + .oo_id_attrs = (NETCONF_ATTR_FAMILY | + NETCONF_ATTR_IFINDEX) +}; + +static struct nl_af_group netconf_groups[] = { + { AF_INET, RTNLGRP_IPV4_NETCONF }, + { AF_INET6, RTNLGRP_IPV6_NETCONF }, + { AF_MPLS, RTNLGRP_MPLS_NETCONF }, + { END_OF_GROUP_LIST }, +}; + +static struct nl_cache_ops rtnl_netconf_ops = { + .co_name = "route/netconf", + .co_hdrsize = sizeof(struct netconfmsg), + .co_msgtypes = { + { RTM_NEWNETCONF, NL_ACT_NEW, "new" }, + { RTM_DELNETCONF, NL_ACT_DEL, "del" }, + { RTM_GETNETCONF, NL_ACT_GET, "get" }, + END_OF_MSGTYPES_LIST, + }, + .co_protocol = NETLINK_ROUTE, + .co_groups = netconf_groups, + .co_request_update = netconf_request_update, + .co_msg_parser = netconf_msg_parser, + .co_obj_ops = &netconf_obj_ops, +}; + +static void __init netconf_init(void) +{ + nl_cache_mngt_register(&rtnl_netconf_ops); +} + +static void __exit netconf_exit(void) +{ + nl_cache_mngt_unregister(&rtnl_netconf_ops); +} + +/** @} */ diff --git a/libnl-route-3.sym b/libnl-route-3.sym index 1fb4371cb..4fd8c0f96 100644 --- a/libnl-route-3.sym +++ b/libnl-route-3.sym @@ -1041,4 +1041,14 @@ global: rtnl_link_inet6_get_flags; rtnl_link_inet6_set_flags; rtnl_u32_get_action; + rtnl_netconf_get_by_idx; + rtnl_netconf_get_all; + rtnl_netconf_get_default; + rtnl_netconf_put; + rtnl_netconf_get_family; + rtnl_netconf_get_ifindex; + rtnl_netconf_get_forwarding; + rtnl_netconf_get_mc_forwarding; + rtnl_netconf_get_rp_filter; + rtnl_netconf_get_input; } libnl_3_2_29; diff --git a/src/nl-monitor.c b/src/nl-monitor.c index 754f8dca0..4400df5ac 100644 --- a/src/nl-monitor.c +++ b/src/nl-monitor.c @@ -14,70 +14,103 @@ #include +static const struct { + enum rtnetlink_groups gr_id; + const char* gr_name; +} known_groups[] = { + { RTNLGRP_LINK, "link" }, + { RTNLGRP_NOTIFY, "notify" }, + { RTNLGRP_NEIGH, "neigh" }, + { RTNLGRP_TC, "tc" }, + { RTNLGRP_IPV4_IFADDR, "ipv4-ifaddr" }, + { RTNLGRP_IPV4_MROUTE, "ipv4-mroute" }, + { RTNLGRP_IPV4_ROUTE, "ipv4-route" }, + { RTNLGRP_IPV6_IFADDR, "ipv6-ifaddr" }, + { RTNLGRP_IPV6_MROUTE, "ipv6-mroute" }, + { RTNLGRP_IPV6_ROUTE, "ipv6-route" }, + { RTNLGRP_IPV6_IFINFO, "ipv6-ifinfo" }, + { RTNLGRP_DECnet_IFADDR, "decnet-ifaddr" }, + { RTNLGRP_DECnet_ROUTE, "decnet-route" }, + { RTNLGRP_IPV6_PREFIX, "ipv6-prefix" }, + { RTNLGRP_IPV4_NETCONF, "ipv4-netconf" }, + { RTNLGRP_IPV6_NETCONF, "ipv6-netconf" }, + { RTNLGRP_MPLS_NETCONF, "mpls-netconf" }, + { RTNLGRP_NONE, NULL } +}; + static void obj_input(struct nl_object *obj, void *arg) { - struct nl_dump_params dp = { - .dp_type = NL_DUMP_STATS, - .dp_fd = stdout, - .dp_dump_msgtype = 1, - }; - - nl_object_dump(obj, &dp); + nl_object_dump(obj, arg); } static int event_input(struct nl_msg *msg, void *arg) { - if (nl_msg_parse(msg, &obj_input, NULL) < 0) + if (nl_msg_parse(msg, &obj_input, arg) < 0) fprintf(stderr, "<> Unknown message type\n"); /* Exit nl_recvmsgs_def() and return to the main select() */ return NL_STOP; } +static void print_usage(void) +{ + int i; + + printf( + "Usage: nl-monitor [OPTION] []\n" + "\n" + "Options\n" + " -f, --format=TYPE Output format { brief | details | stats }\n" + " -h, --help Show this help.\n" + "\n" + ); + printf("Known groups:"); + for (i = 0; known_groups[i].gr_id != RTNLGRP_NONE; i++) + printf(" %s", known_groups[i].gr_name); + printf("\n"); + exit(0); +} + int main(int argc, char *argv[]) { + struct nl_dump_params dp = { + .dp_type = NL_DUMP_STATS, + .dp_fd = stdout, + .dp_dump_msgtype = 1, + }; + struct nl_sock *sock; int err = 1; int i, idx; - static const struct { - enum rtnetlink_groups gr_id; - const char* gr_name; - } known_groups[] = { - { RTNLGRP_LINK, "link" }, - { RTNLGRP_NOTIFY, "notify" }, - { RTNLGRP_NEIGH, "neigh" }, - { RTNLGRP_TC, "tc" }, - { RTNLGRP_IPV4_IFADDR, "ipv4-ifaddr" }, - { RTNLGRP_IPV4_MROUTE, "ipv4-mroute" }, - { RTNLGRP_IPV4_ROUTE, "ipv4-route" }, - { RTNLGRP_IPV6_IFADDR, "ipv6-ifaddr" }, - { RTNLGRP_IPV6_MROUTE, "ipv6-mroute" }, - { RTNLGRP_IPV6_ROUTE, "ipv6-route" }, - { RTNLGRP_IPV6_IFINFO, "ipv6-ifinfo" }, - { RTNLGRP_DECnet_IFADDR, "decnet-ifaddr" }, - { RTNLGRP_DECnet_ROUTE, "decnet-route" }, - { RTNLGRP_IPV6_PREFIX, "ipv6-prefix" }, - { RTNLGRP_NONE, NULL } - }; - sock = nl_cli_alloc_socket(); nl_socket_disable_seq_check(sock); - nl_socket_modify_cb(sock, NL_CB_VALID, NL_CB_CUSTOM, event_input, NULL); - - if (argc > 1 && !strcasecmp(argv[1], "-h")) { - printf("Usage: nl-monitor []\n"); - - printf("Known groups:"); - for (i = 0; known_groups[i].gr_id != RTNLGRP_NONE; i++) - printf(" %s", known_groups[i].gr_name); - printf("\n"); - return 2; + nl_socket_modify_cb(sock, NL_CB_VALID, NL_CB_CUSTOM, event_input, &dp); + + for (;;) { + int c, optidx = 0; + static struct option long_opts[] = { + { "format", 1, 0, 'f' }, + { 0, 0, 0, 0 } + }; + + c = getopt_long(argc, argv, "f:h", long_opts, &optidx); + if (c == -1) + break; + + switch (c) { + case 'f': + dp.dp_type = nl_cli_parse_dumptype(optarg); + break; + default: + print_usage(); + break; + } } nl_cli_connect(sock, NETLINK_ROUTE); - for (idx = 1; argc > idx; idx++) { + for (idx = optind; argc > idx; idx++) { for (i = 0; known_groups[i].gr_id != RTNLGRP_NONE; i++) { if (!strcmp(argv[idx], known_groups[i].gr_name)) {