diff mbox series

[OpenWrt-Devel,netifd] interface: add neighbor config support

Message ID 1555055788-14308-1-git-send-email-meurisalexander@gmail.com
State Accepted
Headers show
Series [OpenWrt-Devel,netifd] interface: add neighbor config support | expand

Commit Message

Alexander Meuris April 12, 2019, 7:56 a.m. UTC
From: meurisa <alexander.meuris@technicolor.com>

The neighbor or neighbor6 network section makes neighbours
configurable via UCI or proto shell handlers. It allows to
install neighbor proxy entries or static neighbor entries

The neighbor or neighbor6 section has the following types:
	interface : declares the logical OpenWrt interface
	ipaddr : the ip address of the neighbor
	mac : the mac address of the neighbor
	proxy : specifies whether the neighbor ia a proxy
		entry (can be 1 or 0)
	router : specifies whether the neighbor is a router
		 (can be 1 or 0)

Signed-off-by: Alexander Meuris <meurisalexander@gmail.com>
Signed-off-by: Hans Dedecker <dedeckeh@gmail.com>
---
 config.c                |  19 +++++-
 interface-ip.c          | 149 +++++++++++++++++++++++++++++++++++++++++++++++-
 interface-ip.h          |  20 ++++++-
 interface.h             |   2 +
 proto-shell.c           |  27 +++++++++
 scripts/netifd-proto.sh |  58 +++++++++++++++++++
 system-dummy.c          |  20 +++++++
 system-linux.c          |  50 +++++++++++++++-
 system.h                |   3 +
 ubus.c                  |  45 +++++++++++++++
 10 files changed, 388 insertions(+), 5 deletions(-)

Comments

Paul Oranje April 13, 2019, 9:17 a.m. UTC | #1
> Op 12 apr. 2019, om 09:56 heeft Alexander Meuris <meurisalexander@gmail.com> het volgende geschreven:
> 
> From: meurisa <alexander.meuris@technicolor.com>
> 
> The neighbor or neighbor6 network section makes neighbours
> configurable via UCI or proto shell handlers. It allows to
> install neighbor proxy entries or static neighbor entries
Out of curiosity: what use-case is served with this change ?

> 
> The neighbor or neighbor6 section has the following types:
> 	interface : declares the logical OpenWrt interface
> 	ipaddr : the ip address of the neighbor
> 	mac : the mac address of the neighbor
> 	proxy : specifies whether the neighbor ia a proxy
> 		entry (can be 1 or 0)
> 	router : specifies whether the neighbor is a router
> 		 (can be 1 or 0)
> 
> Signed-off-by: Alexander Meuris <meurisalexander@gmail.com>
> Signed-off-by: Hans Dedecker <dedeckeh@gmail.com>
> ---
> config.c                |  19 +++++-
> interface-ip.c          | 149 +++++++++++++++++++++++++++++++++++++++++++++++-
> interface-ip.h          |  20 ++++++-
> interface.h             |   2 +
> proto-shell.c           |  27 +++++++++
> scripts/netifd-proto.sh |  58 +++++++++++++++++++
> system-dummy.c          |  20 +++++++
> system-linux.c          |  50 +++++++++++++++-
> system.h                |   3 +
> ubus.c                  |  45 +++++++++++++++
> 10 files changed, 388 insertions(+), 5 deletions(-)
> 
> diff --git a/config.c b/config.c
> index be10379..843c53f 100644
> --- a/config.c
> +++ b/config.c
> @@ -144,6 +144,17 @@ config_parse_route(struct uci_section *s, bool v6)
> }
> 
> static void
> +config_parse_neighbor(struct uci_section *s, bool v6)
> +{
> +	void *neighbor;
> +	blob_buf_init(&b,0);
> +	neighbor = blobmsg_open_array(&b, "neighbor");
> +	uci_to_blob(&b,s, &neighbor_attr_list);
> +	blobmsg_close_array(&b, neighbor);
> +	interface_ip_add_neighbor(NULL, blob_data(b.head), v6);
> +}
> +
> +static void
> config_parse_rule(struct uci_section *s, bool v6)
> {
> 	void *rule;
> @@ -251,7 +262,7 @@ config_init_interfaces(void)
> }
> 
> static void
> -config_init_routes(void)
> +config_init_ip(void)
> {
> 	struct interface *iface;
> 	struct uci_element *e;
> @@ -266,6 +277,10 @@ config_init_routes(void)
> 			config_parse_route(s, false);
> 		else if (!strcmp(s->type, "route6"))
> 			config_parse_route(s, true);
> +		if (!strcmp(s->type, "neighbor"))
> +			config_parse_neighbor(s, false);
> +		else if (!strcmp(s->type, "neighbor6"))
> +			config_parse_neighbor(s, true);
> 	}
> 
> 	vlist_for_each_element(&interfaces, iface, node)
> @@ -417,7 +432,7 @@ config_init_all(void)
> 	device_reset_config();
> 	config_init_devices();
> 	config_init_interfaces();
> -	config_init_routes();
> +	config_init_ip();
> 	config_init_rules();
> 	config_init_globals();
> 	config_init_wireless();
> diff --git a/interface-ip.c b/interface-ip.c
> index 2a183ac..e996aaa 100644
> --- a/interface-ip.c
> +++ b/interface-ip.c
> @@ -20,6 +20,10 @@
> #include <arpa/inet.h>
> #include <netinet/in.h>
> 
> +#ifdef linux
> +#include <netinet/ether.h>
> +#endif
> +
> #include "netifd.h"
> #include "device.h"
> #include "interface.h"
> @@ -64,6 +68,28 @@ const struct uci_blob_param_list route_attr_list = {
> 	.params = route_attr,
> };
> 
> +enum {
> +	NEIGHBOR_INTERFACE,
> +	NEIGHBOR_ADDRESS,
> +	NEIGHBOR_MAC,
> +	NEIGHBOR_PROXY,
> +	NEIGHBOR_ROUTER,
> +	__NEIGHBOR_MAX
> +};
> +
> +static const struct blobmsg_policy neighbor_attr[__NEIGHBOR_MAX]={
> +	[NEIGHBOR_INTERFACE]= { .name = "interface", .type = BLOBMSG_TYPE_STRING},
> +	[NEIGHBOR_ADDRESS]= { .name = "ipaddr", .type = BLOBMSG_TYPE_STRING},
> +	[NEIGHBOR_MAC]= { .name = "mac", .type = BLOBMSG_TYPE_STRING},
> +	[NEIGHBOR_PROXY]= { .name = "proxy", .type = BLOBMSG_TYPE_BOOL},
> +	[NEIGHBOR_ROUTER]= {.name = "router", .type = BLOBMSG_TYPE_BOOL},
> +};
> +
> +const struct uci_blob_param_list neighbor_attr_list = {
> +	.n_params = __NEIGHBOR_MAX,
> +	.params = neighbor_attr,
> +};
> +
> 
> struct list_head prefixes = LIST_HEAD_INIT(prefixes);
> static struct device_prefix *ula_prefix = NULL;
> @@ -299,6 +325,64 @@ interface_set_route_info(struct interface *iface, struct device_route *route)
> }
> 
> void
> +interface_ip_add_neighbor(struct interface *iface, struct blob_attr *attr, bool v6)
> +{
> +	struct interface_ip_settings *ip;
> +	struct blob_attr *tb[__NEIGHBOR_MAX], *cur;
> +	struct device_neighbor *neighbor;
> +	int af = v6 ? AF_INET6: AF_INET;
> +	struct ether_addr *ea;
> +
> +	blobmsg_parse(neighbor_attr, __NEIGHBOR_MAX, tb, blobmsg_data(attr), blobmsg_data_len(attr));
> +
> +	if (!iface) {
> +		if ((cur = tb[NEIGHBOR_INTERFACE]) == NULL)
> +			return;
> +
> +		iface = vlist_find(&interfaces, blobmsg_data(cur), iface, node);
> +
> +		if (!iface)
> +			return;
> +
> +		ip = &iface->config_ip;
> +	} else
> +		ip = &iface->proto_ip;
> +
> +	neighbor = calloc(1,sizeof(*neighbor));
> +	neighbor->flags = v6 ? DEVADDR_INET6 : DEVADDR_INET4;
> +
> +	if (!neighbor)
> +		return;
> +
> +	if ((cur = tb[NEIGHBOR_ADDRESS]) != NULL){
> +		if (!inet_pton(af, blobmsg_data(cur), &neighbor->addr))
> +			goto error;
> +	} else
> +		goto error;
> +
> +	if ((cur = tb[NEIGHBOR_MAC]) != NULL) {
> +		neighbor->flags |= DEVNEIGH_MAC;
> +		ea = ether_aton(blobmsg_data(cur));
> +		if (!ea)
> +			goto error;
> +
> +		memcpy(neighbor->macaddr, ea, 6);
> +	}
> +
> +	if ((cur = tb[NEIGHBOR_PROXY]) != NULL)
> +		neighbor->proxy = blobmsg_get_bool(cur);
> +
> +	if ((cur = tb[NEIGHBOR_ROUTER]) != NULL)
> +		neighbor->router = blobmsg_get_bool(cur);
> +
> +	vlist_add(&ip->neighbor, &neighbor->node, neighbor);
> +	return;
> +
> +error:
> +	free(neighbor);
> +}
> +
> +void
> interface_ip_add_route(struct interface *iface, struct blob_attr *attr, bool v6)
> {
> 	struct interface_ip_settings *ip;
> @@ -429,6 +513,14 @@ addr_cmp(const void *k1, const void *k2, void *ptr)
> }
> 
> static int
> +neighbor_cmp(const void *k1, const void *k2, void *ptr)
> +{
> +	const struct device_neighbor *n1 = k1, *n2 = k2;
> +
> +	return memcmp(&n1->addr, &n2->addr, sizeof(n2->addr));
> +}
> +
> +static int
> route_cmp(const void *k1, const void *k2, void *ptr)
> {
> 	const struct device_route *r1 = k1, *r2 = k2;
> @@ -628,6 +720,44 @@ enable_route(struct interface_ip_settings *ip, struct device_route *route)
> }
> 
> static void
> +interface_update_proto_neighbor(struct vlist_tree *tree,
> +				struct vlist_node * node_new,
> +				struct vlist_node *node_old)
> +{
> +	struct device *dev;
> +	struct device_neighbor *neighbor_old, *neighbor_new;
> +	struct interface_ip_settings *ip;
> +	bool keep = false;
> +
> +	ip = container_of(tree, struct interface_ip_settings, neighbor);
> +	dev = ip->iface->l3_dev.dev;
> +
> +	neighbor_old = container_of(node_old, struct device_neighbor, node);
> +	neighbor_new = container_of(node_new, struct device_neighbor, node);
> +
> +	if (node_old && node_new) {
> +		keep = (!memcmp(neighbor_old->macaddr, neighbor_new->macaddr, sizeof(neighbor_old->macaddr)) &&
> +			(neighbor_old->proxy == neighbor_new->proxy) &&
> +			(neighbor_old->router == neighbor_new->router));
> +	}
> +
> +	if (node_old) {
> +		if (!keep && neighbor_old->enabled)
> +			system_del_neighbor(dev, neighbor_old);
> +
> +		free(neighbor_old);
> +	}
> +
> +	if (node_new) {
> +		if (!keep && ip->enabled)
> +			if (system_add_neighbor(dev, neighbor_new))
> +				neighbor_new->failed = true;
> +
> +		neighbor_new->enabled = ip->enabled;
> +	}
> +}
> +
> +static void
> interface_update_proto_route(struct vlist_tree *tree,
> 			     struct vlist_node *node_new,
> 			     struct vlist_node *node_old)
> @@ -1402,6 +1532,7 @@ void interface_ip_set_enabled(struct interface_ip_settings *ip, bool enabled)
> {
> 	struct device_addr *addr;
> 	struct device_route *route;
> +	struct device_neighbor *neighbor;
> 	struct device *dev;
> 	struct interface *iface;
> 
> @@ -1447,7 +1578,6 @@ void interface_ip_set_enabled(struct interface_ip_settings *ip, bool enabled)
> 
> 		if (!enable_route(ip, route))
> 			_enabled = false;
> -
> 		if (route->enabled == _enabled)
> 			continue;
> 
> @@ -1461,6 +1591,19 @@ void interface_ip_set_enabled(struct interface_ip_settings *ip, bool enabled)
> 		route->enabled = _enabled;
> 	}
> 
> +	vlist_for_each_element(&ip->neighbor, neighbor, node) {
> +		if (neighbor->enabled == enabled)
> +			continue;
> +
> +		if (enabled) {
> +			if(system_add_neighbor(dev, neighbor))
> +				neighbor->failed = true;
> +		} else
> +			system_del_neighbor(dev, neighbor);
> +
> +		neighbor->enabled = enabled;
> +	}
> +
> 	struct device_prefix *c;
> 	struct device_prefix_assignment *a;
> 	list_for_each_entry(c, &prefixes, head)
> @@ -1489,6 +1632,7 @@ interface_ip_update_start(struct interface_ip_settings *ip)
> 	vlist_update(&ip->route);
> 	vlist_update(&ip->addr);
> 	vlist_update(&ip->prefix);
> +	vlist_update(&ip->neighbor);
> }
> 
> void
> @@ -1499,6 +1643,7 @@ interface_ip_update_complete(struct interface_ip_settings *ip)
> 	vlist_flush(&ip->route);
> 	vlist_flush(&ip->addr);
> 	vlist_flush(&ip->prefix);
> +	vlist_flush(&ip->neighbor);
> 	interface_write_resolv_conf();
> }
> 
> @@ -1511,6 +1656,7 @@ interface_ip_flush(struct interface_ip_settings *ip)
> 	vlist_simple_flush_all(&ip->dns_search);
> 	vlist_flush_all(&ip->route);
> 	vlist_flush_all(&ip->addr);
> +	vlist_flush_all(&ip->neighbor);
> 	vlist_flush_all(&ip->prefix);
> }
> 
> @@ -1522,6 +1668,7 @@ __interface_ip_init(struct interface_ip_settings *ip, struct interface *iface)
> 	vlist_simple_init(&ip->dns_search, struct dns_search_domain, node);
> 	vlist_simple_init(&ip->dns_servers, struct dns_server, node);
> 	vlist_init(&ip->route, route_cmp, interface_update_proto_route);
> +	vlist_init(&ip->neighbor, neighbor_cmp, interface_update_proto_neighbor);
> 	vlist_init(&ip->addr, addr_cmp, interface_update_proto_addr);
> 	vlist_init(&ip->prefix, prefix_cmp, interface_update_prefix);
> }
> diff --git a/interface-ip.h b/interface-ip.h
> index 21c6e9b..3f99eb9 100644
> --- a/interface-ip.h
> +++ b/interface-ip.h
> @@ -48,6 +48,9 @@ enum device_addr_flags {
> 
> 	/* route overrides the default route type */
> 	DEVROUTE_TYPE		= (1 << 10),
> +
> +	/* neighbor mac address */
> +	DEVNEIGH_MAC		= (1 << 11),
> };
> 
> union if_addr {
> @@ -106,6 +109,20 @@ struct device_route {
> 	union if_addr source;
> };
> 
> +struct device_neighbor {
> +	struct vlist_node node;
> +
> +	bool failed;
> +	bool proxy;
> +	bool keep;
> +	bool enabled;
> +	bool router;
> +
> +	uint8_t macaddr[6];
> +	enum device_addr_flags flags;
> +	union if_addr addr;
> +};
> +
> struct device_addr {
> 	struct vlist_node node;
> 	bool enabled;
> @@ -150,6 +167,7 @@ struct dns_search_domain {
> };
> 
> extern const struct uci_blob_param_list route_attr_list;
> +extern const struct uci_blob_param_list neighbor_attr_list;
> extern struct list_head prefixes;
> 
> void interface_ip_init(struct interface *iface);
> @@ -158,7 +176,7 @@ void interface_add_dns_search_list(struct interface_ip_settings *ip, struct blob
> void interface_write_resolv_conf(void);
> 
> void interface_ip_add_route(struct interface *iface, struct blob_attr *attr, bool v6);
> -
> +void interface_ip_add_neighbor(struct interface *iface, struct blob_attr *attr, bool v6);
> void interface_ip_update_start(struct interface_ip_settings *ip);
> void interface_ip_update_complete(struct interface_ip_settings *ip);
> void interface_ip_flush(struct interface_ip_settings *ip);
> diff --git a/interface.h b/interface.h
> index 6f10484..3c0c9ed 100644
> --- a/interface.h
> +++ b/interface.h
> @@ -84,6 +84,7 @@ struct interface_ip_settings {
> 	struct vlist_tree addr;
> 	struct vlist_tree route;
> 	struct vlist_tree prefix;
> +	struct vlist_tree neighbor;
> 
> 	struct vlist_simple_tree dns_servers;
> 	struct vlist_simple_tree dns_search;
> @@ -150,6 +151,7 @@ struct interface {
> 	struct interface_ip_settings proto_ip;
> 	struct interface_ip_settings config_ip;
> 	struct vlist_tree host_routes;
> +	struct vlist_tree host_neighbors;
> 
> 	int metric;
> 	int dns_metric;
> diff --git a/proto-shell.c b/proto-shell.c
> index 47a9568..07ec21a 100644
> --- a/proto-shell.c
> +++ b/proto-shell.c
> @@ -414,6 +414,23 @@ proto_shell_parse_route_list(struct interface *iface, struct blob_attr *attr,
> }
> 
> static void
> +proto_shell_parse_neighbor_list(struct interface *iface, struct blob_attr *attr,
> +				bool v6)
> +{
> +	struct blob_attr *cur;
> +	int rem;
> +
> +	blobmsg_for_each_attr(cur, attr, rem) {
> +		if (blobmsg_type(cur) != BLOBMSG_TYPE_TABLE) {
> +			DPRINTF("Ignore wrong neighbor type: %d\n", blobmsg_type(cur));
> +			continue;
> +		}
> +
> +		interface_ip_add_neighbor(iface, cur, v6);
> +	}
> +}
> +
> +static void
> proto_shell_parse_data(struct interface *iface, struct blob_attr *attr)
> {
> 	struct blob_attr *cur;
> @@ -456,6 +473,8 @@ enum {
> 	NOTIFY_HOST,
> 	NOTIFY_DNS,
> 	NOTIFY_DNS_SEARCH,
> +	NOTIFY_NEIGHBORS,
> +	NOTIFY_NEIGHBORS6,
> 	__NOTIFY_LAST
> };
> 
> @@ -477,6 +496,8 @@ static const struct blobmsg_policy notify_attr[__NOTIFY_LAST] = {
> 	[NOTIFY_HOST] = { .name = "host", .type = BLOBMSG_TYPE_STRING },
> 	[NOTIFY_DNS] = { .name = "dns", .type = BLOBMSG_TYPE_ARRAY },
> 	[NOTIFY_DNS_SEARCH] = { .name = "dns_search", .type = BLOBMSG_TYPE_ARRAY },
> +	[NOTIFY_NEIGHBORS]= {.name = "neighbor", .type = BLOBMSG_TYPE_ARRAY},
> +	[NOTIFY_NEIGHBORS6]= {.name = "neighbor6", .type = BLOBMSG_TYPE_ARRAY},
> };
> 
> static int
> @@ -546,6 +567,12 @@ proto_shell_update_link(struct proto_shell_state *state, struct blob_attr *data,
> 	if ((cur = tb[NOTIFY_ROUTES6]) != NULL)
> 		proto_shell_parse_route_list(state->proto.iface, cur, true);
> 
> +	if ((cur = tb[NOTIFY_NEIGHBORS]) != NULL)
> +		proto_shell_parse_neighbor_list(state->proto.iface, cur, false);
> +
> +	if ((cur = tb[NOTIFY_NEIGHBORS6]) != NULL)
> +		proto_shell_parse_neighbor_list(state->proto.iface, cur, true);
> +
> 	if ((cur = tb[NOTIFY_DNS]))
> 		interface_add_dns_server_list(&iface->proto_ip, cur);
> 
> diff --git a/scripts/netifd-proto.sh b/scripts/netifd-proto.sh
> index 31df91f..87d337d 100644
> --- a/scripts/netifd-proto.sh
> +++ b/scripts/netifd-proto.sh
> @@ -64,6 +64,8 @@ proto_init_update() {
> 	PROTO_PREFIX6=
> 	PROTO_DNS=
> 	PROTO_DNS_SEARCH=
> +	PROTO_NEIGHBOR=
> +	PROTO_NEIGHBOR6=
> 	json_init
> 	json_add_int action 0
> 	[ -n "$ifname" -a "*" != "$ifname" ] && json_add_string "ifname" "$ifname"
> @@ -133,6 +135,23 @@ proto_add_ipv6_address() {
> 	append PROTO_IP6ADDR "$address/$mask/$preferred/$valid/$offlink/$class"
> }
> 
> +proto_add_ipv4_neighbor(){
> +	local address="$1"
> +	local mac="$2"
> +	local proxy="$3"
> +
> +	append PROTO_NEIGHBOR "$address/$mac/$proxy"
> +}
> +
> +proto_add_ipv6_neighbor(){
> +	local address="$1"
> +	local mac="$2"
> +	local proxy="$3"
> +	local router="$4"
> +
> +	append PROTO_NEIGHBOR6 "$address/$mac/$proxy/$router"
> +}
> +
> proto_add_ipv4_route() {
> 	local target="$1"
> 	local mask="$2"
> @@ -218,6 +237,43 @@ _proto_push_string() {
> 	json_add_string "" "$1"
> }
> 
> +_proto_push_ipv4_neighbor(){
> +	local str="$1"
> +	local address mac proxy
> +
> +	address="${str%%/*}"
> +	str="${str#*/}"
> +	mac="${str%%/*}"
> +	str="${str#*/}"
> +	proxy="${str%%/*}"
> +
> +	json_add_object ""
> +	json_add_string ipaddr "$address"
> +	[ -n "$mac" ] && json_add_string mac "$mac"
> +	[ -n "$proxy" ] && json_add_boolean proxy "$proxy"
> +	json_close_object
> +}
> +
> +_proto_push_ipv6_neighbor(){
> +	local str="$1"
> +	local address mac proxy router
> +
> +	address="${str%%/*}"
> +	str="${str#*/}"
> +	mac="${str%%/*}"
> +	str="${str#*/}"
> +	proxy="${str%%/*}"
> +	str="${str#*/}"
> +	router="${str%%/*}"
> +
> +	json_add_object ""
> +	json_add_string ipaddr "$address"
> +	[ -n "$mac" ] && json_add_string mac "$mac"
> +	[ -n "$proxy" ] && json_add_boolean proxy "$proxy"
> +	[ -n "$router" ] && json_add_boolean router "$router"
> +	json_close_object
> +}
> +
> _proto_push_route() {
> 	local str="$1";
> 	local target="${str%%/*}"
> @@ -277,6 +333,8 @@ proto_send_update() {
> 	_proto_push_array "ip6prefix" "$PROTO_PREFIX6" _proto_push_string
> 	_proto_push_array "dns" "$PROTO_DNS" _proto_push_string
> 	_proto_push_array "dns_search" "$PROTO_DNS_SEARCH" _proto_push_string
> +	_proto_push_array "neighbor" "$PROTO_NEIGHBOR" _proto_push_ipv4_neighbor
> +	_proto_push_array "neighbor6" "$PROTO_NEIGHBOR6" _proto_push_ipv6_neighbor
> 	_proto_notify "$interface"
> }
> 
> diff --git a/system-dummy.c b/system-dummy.c
> index 11c8ccc..58fd2d0 100644
> --- a/system-dummy.c
> +++ b/system-dummy.c
> @@ -181,6 +181,26 @@ static int system_route_msg(struct device *dev, struct device_route *route, cons
> 	return 0;
> }
> 
> +static int system_neighbor_msg(struct device *dev, struct device_neighbor *neighbor, const char *type)
> +{
> +	char addr[64];
> +	int af = system_get_addr_family(neighbor->flags);
> +	inet_ntop(af, &neighbor->addr.in , addr, sizeof(addr));
> +
> +	D(SYSTEM, "neigh %s %s%s%s %s\n", type, addr, neighbor->proxy ? "proxy " : "",
> +		(neighbor->flags & DEVNEIGH_MAC) ? format_macaddr(neighbor->macaddr) : "",
> +		neighbor->router ? "router": "");
> +}
> +int system_add_neighbor(struct device *dev, struct device_neighbor *neighbor)
> +{
> +	return system_neighbor_msg(dev, neighbor, "add");
> +}
> +
> +int system_del_neighbor(struct device *dev, struct device_neighbor *neighbor)
> +{
> +	return system_neighbor_msg(dev, neighbor, "del");
> +}
> +
> int system_add_route(struct device *dev, struct device_route *route)
> {
> 	return system_route_msg(dev, route, "add");
> diff --git a/system-linux.c b/system-linux.c
> index 82e9928..6a0105e 100644
> --- a/system-linux.c
> +++ b/system-linux.c
> @@ -31,6 +31,7 @@
> #include <netinet/in.h>
> 
> #include <linux/rtnetlink.h>
> +#include <linux/neighbour.h>
> #include <linux/sockios.h>
> #include <linux/ip.h>
> #include <linux/if_addr.h>
> @@ -1023,8 +1024,8 @@ void system_if_clear_state(struct device *dev)
> {
> 	static char buf[256];
> 	char *bridge;
> -
> 	device_set_ifindex(dev, system_if_resolve(dev));
> +
> 	if (dev->external || !dev->ifindex)
> 		return;
> 
> @@ -1046,6 +1047,8 @@ void system_if_clear_state(struct device *dev)
> 	system_if_clear_entries(dev, RTM_GETADDR, AF_INET);
> 	system_if_clear_entries(dev, RTM_GETROUTE, AF_INET6);
> 	system_if_clear_entries(dev, RTM_GETADDR, AF_INET6);
> +	system_if_clear_entries(dev, RTM_GETNEIGH, AF_INET);
> +	system_if_clear_entries(dev, RTM_GETNEIGH, AF_INET6);
> 	system_set_disable_ipv6(dev, "0");
> }
> 
> @@ -1930,6 +1933,51 @@ int system_del_address(struct device *dev, struct device_addr *addr)
> 	return system_addr(dev, addr, RTM_DELADDR);
> }
> 
> +static int system_neigh(struct device *dev, struct device_neighbor *neighbor, int cmd)
> +{
> +	int alen = ((neighbor->flags & DEVADDR_FAMILY) == DEVADDR_INET4) ? 4 : 16;
> +	unsigned int flags = 0;
> +	struct ndmsg ndm = {
> +		.ndm_family = (alen == 4) ? AF_INET : AF_INET6,
> +		.ndm_ifindex = dev->ifindex,
> +		.ndm_state = NUD_PERMANENT,
> +		.ndm_flags = (neighbor->proxy ? NTF_PROXY : 0) | (neighbor->router ? NTF_ROUTER : 0),
> +	};
> +	struct nl_msg *msg;
> +
> +	if (!dev)
> +		return 1;
> +
> +	if (cmd == RTM_NEWNEIGH)
> +		flags |= NLM_F_CREATE | NLM_F_REPLACE;
> +
> +	msg = nlmsg_alloc_simple(cmd, flags);
> +
> +	if (!msg)
> +		return -1;
> +
> +	nlmsg_append(msg, &ndm, sizeof(ndm), 0);
> +
> +	nla_put(msg, NDA_DST, alen, &neighbor->addr);
> +	if (neighbor->flags & DEVNEIGH_MAC)
> +		nla_put(msg, NDA_LLADDR, sizeof(neighbor->macaddr), &neighbor->macaddr);
> +
> +
> +	return system_rtnl_call(msg);
> +}
> +
> +int system_add_neighbor(struct device *dev, struct device_neighbor *neighbor)
> +{
> +	return system_neigh(dev, neighbor, RTM_NEWNEIGH);
> +}
> +
> +int system_del_neighbor(struct device *dev, struct device_neighbor *neighbor)
> +{
> +	int rval = system_neigh(dev, neighbor, RTM_DELNEIGH);
> +	netifd_log_message(L_NOTICE,"return delete %d", rval);
> +	return rval;
> +}
> +
> static int system_rt(struct device *dev, struct device_route *route, int cmd)
> {
> 	int alen = ((route->flags & DEVADDR_FAMILY) == DEVADDR_INET4) ? 4 : 16;
> diff --git a/system.h b/system.h
> index 4d4cf6e..9fefcae 100644
> --- a/system.h
> +++ b/system.h
> @@ -213,6 +213,9 @@ int system_add_route(struct device *dev, struct device_route *route);
> int system_del_route(struct device *dev, struct device_route *route);
> int system_flush_routes(void);
> 
> +int system_add_neighbor(struct device *dev, struct device_neighbor * neighbor);
> +int system_del_neighbor(struct device *dev, struct device_neighbor * neighbor);
> +
> bool system_resolve_rt_type(const char *type, unsigned int *id);
> bool system_resolve_rt_proto(const char *type, unsigned int *id);
> bool system_resolve_rt_table(const char *name, unsigned int *id);
> diff --git a/ubus.c b/ubus.c
> index 32bc1a3..0e86b61 100644
> --- a/ubus.c
> +++ b/ubus.c
> @@ -459,6 +459,43 @@ interface_ip_dump_address_list(struct interface_ip_settings *ip, bool v6, bool e
> }
> 
> static void
> +interface_ip_dump_neighbor_list(struct interface_ip_settings *ip, bool enabled)
> +{
> +	struct device_neighbor *neighbor;
> +	int buflen = 128;
> +	char *buf;
> +	void *r;
> +	int af;
> +
> +	vlist_for_each_element(&ip->neighbor, neighbor, node) {
> +		if (neighbor->enabled != enabled)
> +			continue;
> +
> +		if ((neighbor->flags & DEVADDR_FAMILY) == DEVADDR_INET4)
> +			af = AF_INET;
> +		else
> +			af = AF_INET6;
> +
> +		r = blobmsg_open_table(&b, NULL);
> +
> +		if (neighbor->flags & DEVNEIGH_MAC)
> +			blobmsg_add_string(&b, "mac", format_macaddr(neighbor->macaddr));
> +
> +		buf = blobmsg_alloc_string_buffer(&b , "address", buflen);
> +		inet_ntop(af, &neighbor->addr, buf, buflen);
> +		blobmsg_add_string_buffer(&b);
> +
> +		if (neighbor->proxy)
> +			blobmsg_add_u32(&b, "proxy", neighbor->proxy);
> +
> +		if (neighbor->router)
> +			blobmsg_add_u32(&b, "router", neighbor->router);
> +
> +		blobmsg_close_table(&b, r);
> +	}
> +}
> +
> +static void
> interface_ip_dump_route_list(struct interface_ip_settings *ip, bool enabled)
> {
> 	struct device_route *route;
> @@ -737,6 +774,10 @@ netifd_dump_status(struct interface *iface)
> 		interface_ip_dump_dns_search_list(&iface->config_ip, true);
> 		interface_ip_dump_dns_search_list(&iface->proto_ip, true);
> 		blobmsg_close_array(&b, a);
> +		a = blobmsg_open_array(&b, "neighbors");
> +		interface_ip_dump_neighbor_list(&iface->config_ip, true);
> +		interface_ip_dump_neighbor_list(&iface->proto_ip, true);
> +		blobmsg_close_array(&b, a);
> 
> 		inactive = blobmsg_open_table(&b, "inactive");
> 		a = blobmsg_open_array(&b, "ipv4-address");
> @@ -759,6 +800,10 @@ netifd_dump_status(struct interface *iface)
> 		interface_ip_dump_dns_search_list(&iface->config_ip, false);
> 		interface_ip_dump_dns_search_list(&iface->proto_ip, false);
> 		blobmsg_close_array(&b, a);
> +		a = blobmsg_open_array(&b, "neighbors");
> +		interface_ip_dump_neighbor_list(&iface->config_ip, false);
> +		interface_ip_dump_neighbor_list(&iface->proto_ip, false);
> +		blobmsg_close_array(&b, a);
> 		blobmsg_close_table(&b, inactive);
> 	}
> 
> -- 
> 2.7.4
> 
> 
> _______________________________________________
> openwrt-devel mailing list
> openwrt-devel@lists.openwrt.org
> https://lists.openwrt.org/mailman/listinfo/openwrt-devel
Hans Dedecker April 13, 2019, 12:12 p.m. UTC | #2
On Sat, Apr 13, 2019 at 11:17 AM Paul Oranje <por@oranjevos.nl> wrote:
>
>
> > Op 12 apr. 2019, om 09:56 heeft Alexander Meuris <meurisalexander@gmail.com> het volgende geschreven:
> >
> > From: meurisa <alexander.meuris@technicolor.com>
> >
> > The neighbor or neighbor6 network section makes neighbours
> > configurable via UCI or proto shell handlers. It allows to
> > install neighbor proxy entries or static neighbor entries
> Out of curiosity: what use-case is served with this change ?
Proxy ARP entries can be usefull in some VPN scenarios while static
ARP entries can be usefull for trouble shooting or if a device is
unable to learn a mac address dynamically whatever the reason may be
>
> >
> > The neighbor or neighbor6 section has the following types:
> >       interface : declares the logical OpenWrt interface
> >       ipaddr : the ip address of the neighbor
> >       mac : the mac address of the neighbor
> >       proxy : specifies whether the neighbor ia a proxy
> >               entry (can be 1 or 0)
> >       router : specifies whether the neighbor is a router
> >                (can be 1 or 0)
> >
> > Signed-off-by: Alexander Meuris <meurisalexander@gmail.com>
> > Signed-off-by: Hans Dedecker <dedeckeh@gmail.com>
> > ---
> > config.c                |  19 +++++-
> > interface-ip.c          | 149 +++++++++++++++++++++++++++++++++++++++++++++++-
> > interface-ip.h          |  20 ++++++-
> > interface.h             |   2 +
> > proto-shell.c           |  27 +++++++++
> > scripts/netifd-proto.sh |  58 +++++++++++++++++++
> > system-dummy.c          |  20 +++++++
> > system-linux.c          |  50 +++++++++++++++-
> > system.h                |   3 +
> > ubus.c                  |  45 +++++++++++++++
> > 10 files changed, 388 insertions(+), 5 deletions(-)
> >
> > diff --git a/config.c b/config.c
> > index be10379..843c53f 100644
> > --- a/config.c
> > +++ b/config.c
> > @@ -144,6 +144,17 @@ config_parse_route(struct uci_section *s, bool v6)
> > }
> >
> > static void
> > +config_parse_neighbor(struct uci_section *s, bool v6)
> > +{
> > +     void *neighbor;
> > +     blob_buf_init(&b,0);
> > +     neighbor = blobmsg_open_array(&b, "neighbor");
> > +     uci_to_blob(&b,s, &neighbor_attr_list);
> > +     blobmsg_close_array(&b, neighbor);
> > +     interface_ip_add_neighbor(NULL, blob_data(b.head), v6);
> > +}
> > +
> > +static void
> > config_parse_rule(struct uci_section *s, bool v6)
> > {
> >       void *rule;
> > @@ -251,7 +262,7 @@ config_init_interfaces(void)
> > }
> >
> > static void
> > -config_init_routes(void)
> > +config_init_ip(void)
> > {
> >       struct interface *iface;
> >       struct uci_element *e;
> > @@ -266,6 +277,10 @@ config_init_routes(void)
> >                       config_parse_route(s, false);
> >               else if (!strcmp(s->type, "route6"))
> >                       config_parse_route(s, true);
> > +             if (!strcmp(s->type, "neighbor"))
> > +                     config_parse_neighbor(s, false);
> > +             else if (!strcmp(s->type, "neighbor6"))
> > +                     config_parse_neighbor(s, true);
> >       }
> >
> >       vlist_for_each_element(&interfaces, iface, node)
> > @@ -417,7 +432,7 @@ config_init_all(void)
> >       device_reset_config();
> >       config_init_devices();
> >       config_init_interfaces();
> > -     config_init_routes();
> > +     config_init_ip();
> >       config_init_rules();
> >       config_init_globals();
> >       config_init_wireless();
> > diff --git a/interface-ip.c b/interface-ip.c
> > index 2a183ac..e996aaa 100644
> > --- a/interface-ip.c
> > +++ b/interface-ip.c
> > @@ -20,6 +20,10 @@
> > #include <arpa/inet.h>
> > #include <netinet/in.h>
> >
> > +#ifdef linux
> > +#include <netinet/ether.h>
> > +#endif
> > +
> > #include "netifd.h"
> > #include "device.h"
> > #include "interface.h"
> > @@ -64,6 +68,28 @@ const struct uci_blob_param_list route_attr_list = {
> >       .params = route_attr,
> > };
> >
> > +enum {
> > +     NEIGHBOR_INTERFACE,
> > +     NEIGHBOR_ADDRESS,
> > +     NEIGHBOR_MAC,
> > +     NEIGHBOR_PROXY,
> > +     NEIGHBOR_ROUTER,
> > +     __NEIGHBOR_MAX
> > +};
> > +
> > +static const struct blobmsg_policy neighbor_attr[__NEIGHBOR_MAX]={
> > +     [NEIGHBOR_INTERFACE]= { .name = "interface", .type = BLOBMSG_TYPE_STRING},
> > +     [NEIGHBOR_ADDRESS]= { .name = "ipaddr", .type = BLOBMSG_TYPE_STRING},
> > +     [NEIGHBOR_MAC]= { .name = "mac", .type = BLOBMSG_TYPE_STRING},
> > +     [NEIGHBOR_PROXY]= { .name = "proxy", .type = BLOBMSG_TYPE_BOOL},
> > +     [NEIGHBOR_ROUTER]= {.name = "router", .type = BLOBMSG_TYPE_BOOL},
> > +};
> > +
> > +const struct uci_blob_param_list neighbor_attr_list = {
> > +     .n_params = __NEIGHBOR_MAX,
> > +     .params = neighbor_attr,
> > +};
> > +
> >
> > struct list_head prefixes = LIST_HEAD_INIT(prefixes);
> > static struct device_prefix *ula_prefix = NULL;
> > @@ -299,6 +325,64 @@ interface_set_route_info(struct interface *iface, struct device_route *route)
> > }
> >
> > void
> > +interface_ip_add_neighbor(struct interface *iface, struct blob_attr *attr, bool v6)
> > +{
> > +     struct interface_ip_settings *ip;
> > +     struct blob_attr *tb[__NEIGHBOR_MAX], *cur;
> > +     struct device_neighbor *neighbor;
> > +     int af = v6 ? AF_INET6: AF_INET;
> > +     struct ether_addr *ea;
> > +
> > +     blobmsg_parse(neighbor_attr, __NEIGHBOR_MAX, tb, blobmsg_data(attr), blobmsg_data_len(attr));
> > +
> > +     if (!iface) {
> > +             if ((cur = tb[NEIGHBOR_INTERFACE]) == NULL)
> > +                     return;
> > +
> > +             iface = vlist_find(&interfaces, blobmsg_data(cur), iface, node);
> > +
> > +             if (!iface)
> > +                     return;
> > +
> > +             ip = &iface->config_ip;
> > +     } else
> > +             ip = &iface->proto_ip;
> > +
> > +     neighbor = calloc(1,sizeof(*neighbor));
> > +     neighbor->flags = v6 ? DEVADDR_INET6 : DEVADDR_INET4;
> > +
> > +     if (!neighbor)
> > +             return;
> > +
> > +     if ((cur = tb[NEIGHBOR_ADDRESS]) != NULL){
> > +             if (!inet_pton(af, blobmsg_data(cur), &neighbor->addr))
> > +                     goto error;
> > +     } else
> > +             goto error;
> > +
> > +     if ((cur = tb[NEIGHBOR_MAC]) != NULL) {
> > +             neighbor->flags |= DEVNEIGH_MAC;
> > +             ea = ether_aton(blobmsg_data(cur));
> > +             if (!ea)
> > +                     goto error;
> > +
> > +             memcpy(neighbor->macaddr, ea, 6);
> > +     }
> > +
> > +     if ((cur = tb[NEIGHBOR_PROXY]) != NULL)
> > +             neighbor->proxy = blobmsg_get_bool(cur);
> > +
> > +     if ((cur = tb[NEIGHBOR_ROUTER]) != NULL)
> > +             neighbor->router = blobmsg_get_bool(cur);
> > +
> > +     vlist_add(&ip->neighbor, &neighbor->node, neighbor);
> > +     return;
> > +
> > +error:
> > +     free(neighbor);
> > +}
> > +
> > +void
> > interface_ip_add_route(struct interface *iface, struct blob_attr *attr, bool v6)
> > {
> >       struct interface_ip_settings *ip;
> > @@ -429,6 +513,14 @@ addr_cmp(const void *k1, const void *k2, void *ptr)
> > }
> >
> > static int
> > +neighbor_cmp(const void *k1, const void *k2, void *ptr)
> > +{
> > +     const struct device_neighbor *n1 = k1, *n2 = k2;
> > +
> > +     return memcmp(&n1->addr, &n2->addr, sizeof(n2->addr));
> > +}
> > +
> > +static int
> > route_cmp(const void *k1, const void *k2, void *ptr)
> > {
> >       const struct device_route *r1 = k1, *r2 = k2;
> > @@ -628,6 +720,44 @@ enable_route(struct interface_ip_settings *ip, struct device_route *route)
> > }
> >
> > static void
> > +interface_update_proto_neighbor(struct vlist_tree *tree,
> > +                             struct vlist_node * node_new,
> > +                             struct vlist_node *node_old)
> > +{
> > +     struct device *dev;
> > +     struct device_neighbor *neighbor_old, *neighbor_new;
> > +     struct interface_ip_settings *ip;
> > +     bool keep = false;
> > +
> > +     ip = container_of(tree, struct interface_ip_settings, neighbor);
> > +     dev = ip->iface->l3_dev.dev;
> > +
> > +     neighbor_old = container_of(node_old, struct device_neighbor, node);
> > +     neighbor_new = container_of(node_new, struct device_neighbor, node);
> > +
> > +     if (node_old && node_new) {
> > +             keep = (!memcmp(neighbor_old->macaddr, neighbor_new->macaddr, sizeof(neighbor_old->macaddr)) &&
> > +                     (neighbor_old->proxy == neighbor_new->proxy) &&
> > +                     (neighbor_old->router == neighbor_new->router));
> > +     }
> > +
> > +     if (node_old) {
> > +             if (!keep && neighbor_old->enabled)
> > +                     system_del_neighbor(dev, neighbor_old);
> > +
> > +             free(neighbor_old);
> > +     }
> > +
> > +     if (node_new) {
> > +             if (!keep && ip->enabled)
> > +                     if (system_add_neighbor(dev, neighbor_new))
> > +                             neighbor_new->failed = true;
> > +
> > +             neighbor_new->enabled = ip->enabled;
> > +     }
> > +}
> > +
> > +static void
> > interface_update_proto_route(struct vlist_tree *tree,
> >                            struct vlist_node *node_new,
> >                            struct vlist_node *node_old)
> > @@ -1402,6 +1532,7 @@ void interface_ip_set_enabled(struct interface_ip_settings *ip, bool enabled)
> > {
> >       struct device_addr *addr;
> >       struct device_route *route;
> > +     struct device_neighbor *neighbor;
> >       struct device *dev;
> >       struct interface *iface;
> >
> > @@ -1447,7 +1578,6 @@ void interface_ip_set_enabled(struct interface_ip_settings *ip, bool enabled)
> >
> >               if (!enable_route(ip, route))
> >                       _enabled = false;
> > -
> >               if (route->enabled == _enabled)
> >                       continue;
> >
> > @@ -1461,6 +1591,19 @@ void interface_ip_set_enabled(struct interface_ip_settings *ip, bool enabled)
> >               route->enabled = _enabled;
> >       }
> >
> > +     vlist_for_each_element(&ip->neighbor, neighbor, node) {
> > +             if (neighbor->enabled == enabled)
> > +                     continue;
> > +
> > +             if (enabled) {
> > +                     if(system_add_neighbor(dev, neighbor))
> > +                             neighbor->failed = true;
> > +             } else
> > +                     system_del_neighbor(dev, neighbor);
> > +
> > +             neighbor->enabled = enabled;
> > +     }
> > +
> >       struct device_prefix *c;
> >       struct device_prefix_assignment *a;
> >       list_for_each_entry(c, &prefixes, head)
> > @@ -1489,6 +1632,7 @@ interface_ip_update_start(struct interface_ip_settings *ip)
> >       vlist_update(&ip->route);
> >       vlist_update(&ip->addr);
> >       vlist_update(&ip->prefix);
> > +     vlist_update(&ip->neighbor);
> > }
> >
> > void
> > @@ -1499,6 +1643,7 @@ interface_ip_update_complete(struct interface_ip_settings *ip)
> >       vlist_flush(&ip->route);
> >       vlist_flush(&ip->addr);
> >       vlist_flush(&ip->prefix);
> > +     vlist_flush(&ip->neighbor);
> >       interface_write_resolv_conf();
> > }
> >
> > @@ -1511,6 +1656,7 @@ interface_ip_flush(struct interface_ip_settings *ip)
> >       vlist_simple_flush_all(&ip->dns_search);
> >       vlist_flush_all(&ip->route);
> >       vlist_flush_all(&ip->addr);
> > +     vlist_flush_all(&ip->neighbor);
> >       vlist_flush_all(&ip->prefix);
> > }
> >
> > @@ -1522,6 +1668,7 @@ __interface_ip_init(struct interface_ip_settings *ip, struct interface *iface)
> >       vlist_simple_init(&ip->dns_search, struct dns_search_domain, node);
> >       vlist_simple_init(&ip->dns_servers, struct dns_server, node);
> >       vlist_init(&ip->route, route_cmp, interface_update_proto_route);
> > +     vlist_init(&ip->neighbor, neighbor_cmp, interface_update_proto_neighbor);
> >       vlist_init(&ip->addr, addr_cmp, interface_update_proto_addr);
> >       vlist_init(&ip->prefix, prefix_cmp, interface_update_prefix);
> > }
> > diff --git a/interface-ip.h b/interface-ip.h
> > index 21c6e9b..3f99eb9 100644
> > --- a/interface-ip.h
> > +++ b/interface-ip.h
> > @@ -48,6 +48,9 @@ enum device_addr_flags {
> >
> >       /* route overrides the default route type */
> >       DEVROUTE_TYPE           = (1 << 10),
> > +
> > +     /* neighbor mac address */
> > +     DEVNEIGH_MAC            = (1 << 11),
> > };
> >
> > union if_addr {
> > @@ -106,6 +109,20 @@ struct device_route {
> >       union if_addr source;
> > };
> >
> > +struct device_neighbor {
> > +     struct vlist_node node;
> > +
> > +     bool failed;
> > +     bool proxy;
> > +     bool keep;
> > +     bool enabled;
> > +     bool router;
> > +
> > +     uint8_t macaddr[6];
> > +     enum device_addr_flags flags;
> > +     union if_addr addr;
> > +};
> > +
> > struct device_addr {
> >       struct vlist_node node;
> >       bool enabled;
> > @@ -150,6 +167,7 @@ struct dns_search_domain {
> > };
> >
> > extern const struct uci_blob_param_list route_attr_list;
> > +extern const struct uci_blob_param_list neighbor_attr_list;
> > extern struct list_head prefixes;
> >
> > void interface_ip_init(struct interface *iface);
> > @@ -158,7 +176,7 @@ void interface_add_dns_search_list(struct interface_ip_settings *ip, struct blob
> > void interface_write_resolv_conf(void);
> >
> > void interface_ip_add_route(struct interface *iface, struct blob_attr *attr, bool v6);
> > -
> > +void interface_ip_add_neighbor(struct interface *iface, struct blob_attr *attr, bool v6);
> > void interface_ip_update_start(struct interface_ip_settings *ip);
> > void interface_ip_update_complete(struct interface_ip_settings *ip);
> > void interface_ip_flush(struct interface_ip_settings *ip);
> > diff --git a/interface.h b/interface.h
> > index 6f10484..3c0c9ed 100644
> > --- a/interface.h
> > +++ b/interface.h
> > @@ -84,6 +84,7 @@ struct interface_ip_settings {
> >       struct vlist_tree addr;
> >       struct vlist_tree route;
> >       struct vlist_tree prefix;
> > +     struct vlist_tree neighbor;
> >
> >       struct vlist_simple_tree dns_servers;
> >       struct vlist_simple_tree dns_search;
> > @@ -150,6 +151,7 @@ struct interface {
> >       struct interface_ip_settings proto_ip;
> >       struct interface_ip_settings config_ip;
> >       struct vlist_tree host_routes;
> > +     struct vlist_tree host_neighbors;
> >
> >       int metric;
> >       int dns_metric;
> > diff --git a/proto-shell.c b/proto-shell.c
> > index 47a9568..07ec21a 100644
> > --- a/proto-shell.c
> > +++ b/proto-shell.c
> > @@ -414,6 +414,23 @@ proto_shell_parse_route_list(struct interface *iface, struct blob_attr *attr,
> > }
> >
> > static void
> > +proto_shell_parse_neighbor_list(struct interface *iface, struct blob_attr *attr,
> > +                             bool v6)
> > +{
> > +     struct blob_attr *cur;
> > +     int rem;
> > +
> > +     blobmsg_for_each_attr(cur, attr, rem) {
> > +             if (blobmsg_type(cur) != BLOBMSG_TYPE_TABLE) {
> > +                     DPRINTF("Ignore wrong neighbor type: %d\n", blobmsg_type(cur));
> > +                     continue;
> > +             }
> > +
> > +             interface_ip_add_neighbor(iface, cur, v6);
> > +     }
> > +}
> > +
> > +static void
> > proto_shell_parse_data(struct interface *iface, struct blob_attr *attr)
> > {
> >       struct blob_attr *cur;
> > @@ -456,6 +473,8 @@ enum {
> >       NOTIFY_HOST,
> >       NOTIFY_DNS,
> >       NOTIFY_DNS_SEARCH,
> > +     NOTIFY_NEIGHBORS,
> > +     NOTIFY_NEIGHBORS6,
> >       __NOTIFY_LAST
> > };
> >
> > @@ -477,6 +496,8 @@ static const struct blobmsg_policy notify_attr[__NOTIFY_LAST] = {
> >       [NOTIFY_HOST] = { .name = "host", .type = BLOBMSG_TYPE_STRING },
> >       [NOTIFY_DNS] = { .name = "dns", .type = BLOBMSG_TYPE_ARRAY },
> >       [NOTIFY_DNS_SEARCH] = { .name = "dns_search", .type = BLOBMSG_TYPE_ARRAY },
> > +     [NOTIFY_NEIGHBORS]= {.name = "neighbor", .type = BLOBMSG_TYPE_ARRAY},
> > +     [NOTIFY_NEIGHBORS6]= {.name = "neighbor6", .type = BLOBMSG_TYPE_ARRAY},
> > };
> >
> > static int
> > @@ -546,6 +567,12 @@ proto_shell_update_link(struct proto_shell_state *state, struct blob_attr *data,
> >       if ((cur = tb[NOTIFY_ROUTES6]) != NULL)
> >               proto_shell_parse_route_list(state->proto.iface, cur, true);
> >
> > +     if ((cur = tb[NOTIFY_NEIGHBORS]) != NULL)
> > +             proto_shell_parse_neighbor_list(state->proto.iface, cur, false);
> > +
> > +     if ((cur = tb[NOTIFY_NEIGHBORS6]) != NULL)
> > +             proto_shell_parse_neighbor_list(state->proto.iface, cur, true);
> > +
> >       if ((cur = tb[NOTIFY_DNS]))
> >               interface_add_dns_server_list(&iface->proto_ip, cur);
> >
> > diff --git a/scripts/netifd-proto.sh b/scripts/netifd-proto.sh
> > index 31df91f..87d337d 100644
> > --- a/scripts/netifd-proto.sh
> > +++ b/scripts/netifd-proto.sh
> > @@ -64,6 +64,8 @@ proto_init_update() {
> >       PROTO_PREFIX6=
> >       PROTO_DNS=
> >       PROTO_DNS_SEARCH=
> > +     PROTO_NEIGHBOR=
> > +     PROTO_NEIGHBOR6=
> >       json_init
> >       json_add_int action 0
> >       [ -n "$ifname" -a "*" != "$ifname" ] && json_add_string "ifname" "$ifname"
> > @@ -133,6 +135,23 @@ proto_add_ipv6_address() {
> >       append PROTO_IP6ADDR "$address/$mask/$preferred/$valid/$offlink/$class"
> > }
> >
> > +proto_add_ipv4_neighbor(){
> > +     local address="$1"
> > +     local mac="$2"
> > +     local proxy="$3"
> > +
> > +     append PROTO_NEIGHBOR "$address/$mac/$proxy"
> > +}
> > +
> > +proto_add_ipv6_neighbor(){
> > +     local address="$1"
> > +     local mac="$2"
> > +     local proxy="$3"
> > +     local router="$4"
> > +
> > +     append PROTO_NEIGHBOR6 "$address/$mac/$proxy/$router"
> > +}
> > +
> > proto_add_ipv4_route() {
> >       local target="$1"
> >       local mask="$2"
> > @@ -218,6 +237,43 @@ _proto_push_string() {
> >       json_add_string "" "$1"
> > }
> >
> > +_proto_push_ipv4_neighbor(){
> > +     local str="$1"
> > +     local address mac proxy
> > +
> > +     address="${str%%/*}"
> > +     str="${str#*/}"
> > +     mac="${str%%/*}"
> > +     str="${str#*/}"
> > +     proxy="${str%%/*}"
> > +
> > +     json_add_object ""
> > +     json_add_string ipaddr "$address"
> > +     [ -n "$mac" ] && json_add_string mac "$mac"
> > +     [ -n "$proxy" ] && json_add_boolean proxy "$proxy"
> > +     json_close_object
> > +}
> > +
> > +_proto_push_ipv6_neighbor(){
> > +     local str="$1"
> > +     local address mac proxy router
> > +
> > +     address="${str%%/*}"
> > +     str="${str#*/}"
> > +     mac="${str%%/*}"
> > +     str="${str#*/}"
> > +     proxy="${str%%/*}"
> > +     str="${str#*/}"
> > +     router="${str%%/*}"
> > +
> > +     json_add_object ""
> > +     json_add_string ipaddr "$address"
> > +     [ -n "$mac" ] && json_add_string mac "$mac"
> > +     [ -n "$proxy" ] && json_add_boolean proxy "$proxy"
> > +     [ -n "$router" ] && json_add_boolean router "$router"
> > +     json_close_object
> > +}
> > +
> > _proto_push_route() {
> >       local str="$1";
> >       local target="${str%%/*}"
> > @@ -277,6 +333,8 @@ proto_send_update() {
> >       _proto_push_array "ip6prefix" "$PROTO_PREFIX6" _proto_push_string
> >       _proto_push_array "dns" "$PROTO_DNS" _proto_push_string
> >       _proto_push_array "dns_search" "$PROTO_DNS_SEARCH" _proto_push_string
> > +     _proto_push_array "neighbor" "$PROTO_NEIGHBOR" _proto_push_ipv4_neighbor
> > +     _proto_push_array "neighbor6" "$PROTO_NEIGHBOR6" _proto_push_ipv6_neighbor
> >       _proto_notify "$interface"
> > }
> >
> > diff --git a/system-dummy.c b/system-dummy.c
> > index 11c8ccc..58fd2d0 100644
> > --- a/system-dummy.c
> > +++ b/system-dummy.c
> > @@ -181,6 +181,26 @@ static int system_route_msg(struct device *dev, struct device_route *route, cons
> >       return 0;
> > }
> >
> > +static int system_neighbor_msg(struct device *dev, struct device_neighbor *neighbor, const char *type)
> > +{
> > +     char addr[64];
> > +     int af = system_get_addr_family(neighbor->flags);
> > +     inet_ntop(af, &neighbor->addr.in , addr, sizeof(addr));
> > +
> > +     D(SYSTEM, "neigh %s %s%s%s %s\n", type, addr, neighbor->proxy ? "proxy " : "",
> > +             (neighbor->flags & DEVNEIGH_MAC) ? format_macaddr(neighbor->macaddr) : "",
> > +             neighbor->router ? "router": "");
> > +}
> > +int system_add_neighbor(struct device *dev, struct device_neighbor *neighbor)
> > +{
> > +     return system_neighbor_msg(dev, neighbor, "add");
> > +}
> > +
> > +int system_del_neighbor(struct device *dev, struct device_neighbor *neighbor)
> > +{
> > +     return system_neighbor_msg(dev, neighbor, "del");
> > +}
> > +
> > int system_add_route(struct device *dev, struct device_route *route)
> > {
> >       return system_route_msg(dev, route, "add");
> > diff --git a/system-linux.c b/system-linux.c
> > index 82e9928..6a0105e 100644
> > --- a/system-linux.c
> > +++ b/system-linux.c
> > @@ -31,6 +31,7 @@
> > #include <netinet/in.h>
> >
> > #include <linux/rtnetlink.h>
> > +#include <linux/neighbour.h>
> > #include <linux/sockios.h>
> > #include <linux/ip.h>
> > #include <linux/if_addr.h>
> > @@ -1023,8 +1024,8 @@ void system_if_clear_state(struct device *dev)
> > {
> >       static char buf[256];
> >       char *bridge;
> > -
> >       device_set_ifindex(dev, system_if_resolve(dev));
> > +
> >       if (dev->external || !dev->ifindex)
> >               return;
> >
> > @@ -1046,6 +1047,8 @@ void system_if_clear_state(struct device *dev)
> >       system_if_clear_entries(dev, RTM_GETADDR, AF_INET);
> >       system_if_clear_entries(dev, RTM_GETROUTE, AF_INET6);
> >       system_if_clear_entries(dev, RTM_GETADDR, AF_INET6);
> > +     system_if_clear_entries(dev, RTM_GETNEIGH, AF_INET);
> > +     system_if_clear_entries(dev, RTM_GETNEIGH, AF_INET6);
> >       system_set_disable_ipv6(dev, "0");
> > }
> >
> > @@ -1930,6 +1933,51 @@ int system_del_address(struct device *dev, struct device_addr *addr)
> >       return system_addr(dev, addr, RTM_DELADDR);
> > }
> >
> > +static int system_neigh(struct device *dev, struct device_neighbor *neighbor, int cmd)
> > +{
> > +     int alen = ((neighbor->flags & DEVADDR_FAMILY) == DEVADDR_INET4) ? 4 : 16;
> > +     unsigned int flags = 0;
> > +     struct ndmsg ndm = {
> > +             .ndm_family = (alen == 4) ? AF_INET : AF_INET6,
> > +             .ndm_ifindex = dev->ifindex,
> > +             .ndm_state = NUD_PERMANENT,
> > +             .ndm_flags = (neighbor->proxy ? NTF_PROXY : 0) | (neighbor->router ? NTF_ROUTER : 0),
> > +     };
> > +     struct nl_msg *msg;
> > +
> > +     if (!dev)
> > +             return 1;
> > +
> > +     if (cmd == RTM_NEWNEIGH)
> > +             flags |= NLM_F_CREATE | NLM_F_REPLACE;
> > +
> > +     msg = nlmsg_alloc_simple(cmd, flags);
> > +
> > +     if (!msg)
> > +             return -1;
> > +
> > +     nlmsg_append(msg, &ndm, sizeof(ndm), 0);
> > +
> > +     nla_put(msg, NDA_DST, alen, &neighbor->addr);
> > +     if (neighbor->flags & DEVNEIGH_MAC)
> > +             nla_put(msg, NDA_LLADDR, sizeof(neighbor->macaddr), &neighbor->macaddr);
> > +
> > +
> > +     return system_rtnl_call(msg);
> > +}
> > +
> > +int system_add_neighbor(struct device *dev, struct device_neighbor *neighbor)
> > +{
> > +     return system_neigh(dev, neighbor, RTM_NEWNEIGH);
> > +}
> > +
> > +int system_del_neighbor(struct device *dev, struct device_neighbor *neighbor)
> > +{
> > +     int rval = system_neigh(dev, neighbor, RTM_DELNEIGH);
> > +     netifd_log_message(L_NOTICE,"return delete %d", rval);
> > +     return rval;
> > +}
> > +
> > static int system_rt(struct device *dev, struct device_route *route, int cmd)
> > {
> >       int alen = ((route->flags & DEVADDR_FAMILY) == DEVADDR_INET4) ? 4 : 16;
> > diff --git a/system.h b/system.h
> > index 4d4cf6e..9fefcae 100644
> > --- a/system.h
> > +++ b/system.h
> > @@ -213,6 +213,9 @@ int system_add_route(struct device *dev, struct device_route *route);
> > int system_del_route(struct device *dev, struct device_route *route);
> > int system_flush_routes(void);
> >
> > +int system_add_neighbor(struct device *dev, struct device_neighbor * neighbor);
> > +int system_del_neighbor(struct device *dev, struct device_neighbor * neighbor);
> > +
> > bool system_resolve_rt_type(const char *type, unsigned int *id);
> > bool system_resolve_rt_proto(const char *type, unsigned int *id);
> > bool system_resolve_rt_table(const char *name, unsigned int *id);
> > diff --git a/ubus.c b/ubus.c
> > index 32bc1a3..0e86b61 100644
> > --- a/ubus.c
> > +++ b/ubus.c
> > @@ -459,6 +459,43 @@ interface_ip_dump_address_list(struct interface_ip_settings *ip, bool v6, bool e
> > }
> >
> > static void
> > +interface_ip_dump_neighbor_list(struct interface_ip_settings *ip, bool enabled)
> > +{
> > +     struct device_neighbor *neighbor;
> > +     int buflen = 128;
> > +     char *buf;
> > +     void *r;
> > +     int af;
> > +
> > +     vlist_for_each_element(&ip->neighbor, neighbor, node) {
> > +             if (neighbor->enabled != enabled)
> > +                     continue;
> > +
> > +             if ((neighbor->flags & DEVADDR_FAMILY) == DEVADDR_INET4)
> > +                     af = AF_INET;
> > +             else
> > +                     af = AF_INET6;
> > +
> > +             r = blobmsg_open_table(&b, NULL);
> > +
> > +             if (neighbor->flags & DEVNEIGH_MAC)
> > +                     blobmsg_add_string(&b, "mac", format_macaddr(neighbor->macaddr));
> > +
> > +             buf = blobmsg_alloc_string_buffer(&b , "address", buflen);
> > +             inet_ntop(af, &neighbor->addr, buf, buflen);
> > +             blobmsg_add_string_buffer(&b);
> > +
> > +             if (neighbor->proxy)
> > +                     blobmsg_add_u32(&b, "proxy", neighbor->proxy);
> > +
> > +             if (neighbor->router)
> > +                     blobmsg_add_u32(&b, "router", neighbor->router);
> > +
> > +             blobmsg_close_table(&b, r);
> > +     }
> > +}
> > +
> > +static void
> > interface_ip_dump_route_list(struct interface_ip_settings *ip, bool enabled)
> > {
> >       struct device_route *route;
> > @@ -737,6 +774,10 @@ netifd_dump_status(struct interface *iface)
> >               interface_ip_dump_dns_search_list(&iface->config_ip, true);
> >               interface_ip_dump_dns_search_list(&iface->proto_ip, true);
> >               blobmsg_close_array(&b, a);
> > +             a = blobmsg_open_array(&b, "neighbors");
> > +             interface_ip_dump_neighbor_list(&iface->config_ip, true);
> > +             interface_ip_dump_neighbor_list(&iface->proto_ip, true);
> > +             blobmsg_close_array(&b, a);
> >
> >               inactive = blobmsg_open_table(&b, "inactive");
> >               a = blobmsg_open_array(&b, "ipv4-address");
> > @@ -759,6 +800,10 @@ netifd_dump_status(struct interface *iface)
> >               interface_ip_dump_dns_search_list(&iface->config_ip, false);
> >               interface_ip_dump_dns_search_list(&iface->proto_ip, false);
> >               blobmsg_close_array(&b, a);
> > +             a = blobmsg_open_array(&b, "neighbors");
> > +             interface_ip_dump_neighbor_list(&iface->config_ip, false);
> > +             interface_ip_dump_neighbor_list(&iface->proto_ip, false);
> > +             blobmsg_close_array(&b, a);
> >               blobmsg_close_table(&b, inactive);
> >       }
> >
> > --
> > 2.7.4
> >
> >
> > _______________________________________________
> > openwrt-devel mailing list
> > openwrt-devel@lists.openwrt.org
> > https://lists.openwrt.org/mailman/listinfo/openwrt-devel
>
diff mbox series

Patch

diff --git a/config.c b/config.c
index be10379..843c53f 100644
--- a/config.c
+++ b/config.c
@@ -144,6 +144,17 @@  config_parse_route(struct uci_section *s, bool v6)
 }
 
 static void
+config_parse_neighbor(struct uci_section *s, bool v6)
+{
+	void *neighbor;
+	blob_buf_init(&b,0);
+	neighbor = blobmsg_open_array(&b, "neighbor");
+	uci_to_blob(&b,s, &neighbor_attr_list);
+	blobmsg_close_array(&b, neighbor);
+	interface_ip_add_neighbor(NULL, blob_data(b.head), v6);
+}
+
+static void
 config_parse_rule(struct uci_section *s, bool v6)
 {
 	void *rule;
@@ -251,7 +262,7 @@  config_init_interfaces(void)
 }
 
 static void
-config_init_routes(void)
+config_init_ip(void)
 {
 	struct interface *iface;
 	struct uci_element *e;
@@ -266,6 +277,10 @@  config_init_routes(void)
 			config_parse_route(s, false);
 		else if (!strcmp(s->type, "route6"))
 			config_parse_route(s, true);
+		if (!strcmp(s->type, "neighbor"))
+			config_parse_neighbor(s, false);
+		else if (!strcmp(s->type, "neighbor6"))
+			config_parse_neighbor(s, true);
 	}
 
 	vlist_for_each_element(&interfaces, iface, node)
@@ -417,7 +432,7 @@  config_init_all(void)
 	device_reset_config();
 	config_init_devices();
 	config_init_interfaces();
-	config_init_routes();
+	config_init_ip();
 	config_init_rules();
 	config_init_globals();
 	config_init_wireless();
diff --git a/interface-ip.c b/interface-ip.c
index 2a183ac..e996aaa 100644
--- a/interface-ip.c
+++ b/interface-ip.c
@@ -20,6 +20,10 @@ 
 #include <arpa/inet.h>
 #include <netinet/in.h>
 
+#ifdef linux
+#include <netinet/ether.h>
+#endif
+
 #include "netifd.h"
 #include "device.h"
 #include "interface.h"
@@ -64,6 +68,28 @@  const struct uci_blob_param_list route_attr_list = {
 	.params = route_attr,
 };
 
+enum {
+	NEIGHBOR_INTERFACE,
+	NEIGHBOR_ADDRESS,
+	NEIGHBOR_MAC,
+	NEIGHBOR_PROXY,
+	NEIGHBOR_ROUTER,
+	__NEIGHBOR_MAX
+};
+
+static const struct blobmsg_policy neighbor_attr[__NEIGHBOR_MAX]={
+	[NEIGHBOR_INTERFACE]= { .name = "interface", .type = BLOBMSG_TYPE_STRING},
+	[NEIGHBOR_ADDRESS]= { .name = "ipaddr", .type = BLOBMSG_TYPE_STRING},
+	[NEIGHBOR_MAC]= { .name = "mac", .type = BLOBMSG_TYPE_STRING},
+	[NEIGHBOR_PROXY]= { .name = "proxy", .type = BLOBMSG_TYPE_BOOL},
+	[NEIGHBOR_ROUTER]= {.name = "router", .type = BLOBMSG_TYPE_BOOL},
+};
+
+const struct uci_blob_param_list neighbor_attr_list = {
+	.n_params = __NEIGHBOR_MAX,
+	.params = neighbor_attr,
+};
+
 
 struct list_head prefixes = LIST_HEAD_INIT(prefixes);
 static struct device_prefix *ula_prefix = NULL;
@@ -299,6 +325,64 @@  interface_set_route_info(struct interface *iface, struct device_route *route)
 }
 
 void
+interface_ip_add_neighbor(struct interface *iface, struct blob_attr *attr, bool v6)
+{
+	struct interface_ip_settings *ip;
+	struct blob_attr *tb[__NEIGHBOR_MAX], *cur;
+	struct device_neighbor *neighbor;
+	int af = v6 ? AF_INET6: AF_INET;
+	struct ether_addr *ea;
+
+	blobmsg_parse(neighbor_attr, __NEIGHBOR_MAX, tb, blobmsg_data(attr), blobmsg_data_len(attr));
+
+	if (!iface) {
+		if ((cur = tb[NEIGHBOR_INTERFACE]) == NULL)
+			return;
+
+		iface = vlist_find(&interfaces, blobmsg_data(cur), iface, node);
+
+		if (!iface)
+			return;
+
+		ip = &iface->config_ip;
+	} else
+		ip = &iface->proto_ip;
+
+	neighbor = calloc(1,sizeof(*neighbor));
+	neighbor->flags = v6 ? DEVADDR_INET6 : DEVADDR_INET4;
+
+	if (!neighbor)
+		return;
+
+	if ((cur = tb[NEIGHBOR_ADDRESS]) != NULL){
+		if (!inet_pton(af, blobmsg_data(cur), &neighbor->addr))
+			goto error;
+	} else
+		goto error;
+
+	if ((cur = tb[NEIGHBOR_MAC]) != NULL) {
+		neighbor->flags |= DEVNEIGH_MAC;
+		ea = ether_aton(blobmsg_data(cur));
+		if (!ea)
+			goto error;
+
+		memcpy(neighbor->macaddr, ea, 6);
+	}
+
+	if ((cur = tb[NEIGHBOR_PROXY]) != NULL)
+		neighbor->proxy = blobmsg_get_bool(cur);
+
+	if ((cur = tb[NEIGHBOR_ROUTER]) != NULL)
+		neighbor->router = blobmsg_get_bool(cur);
+
+	vlist_add(&ip->neighbor, &neighbor->node, neighbor);
+	return;
+
+error:
+	free(neighbor);
+}
+
+void
 interface_ip_add_route(struct interface *iface, struct blob_attr *attr, bool v6)
 {
 	struct interface_ip_settings *ip;
@@ -429,6 +513,14 @@  addr_cmp(const void *k1, const void *k2, void *ptr)
 }
 
 static int
+neighbor_cmp(const void *k1, const void *k2, void *ptr)
+{
+	const struct device_neighbor *n1 = k1, *n2 = k2;
+
+	return memcmp(&n1->addr, &n2->addr, sizeof(n2->addr));
+}
+
+static int
 route_cmp(const void *k1, const void *k2, void *ptr)
 {
 	const struct device_route *r1 = k1, *r2 = k2;
@@ -628,6 +720,44 @@  enable_route(struct interface_ip_settings *ip, struct device_route *route)
 }
 
 static void
+interface_update_proto_neighbor(struct vlist_tree *tree,
+				struct vlist_node * node_new,
+				struct vlist_node *node_old)
+{
+	struct device *dev;
+	struct device_neighbor *neighbor_old, *neighbor_new;
+	struct interface_ip_settings *ip;
+	bool keep = false;
+
+	ip = container_of(tree, struct interface_ip_settings, neighbor);
+	dev = ip->iface->l3_dev.dev;
+
+	neighbor_old = container_of(node_old, struct device_neighbor, node);
+	neighbor_new = container_of(node_new, struct device_neighbor, node);
+
+	if (node_old && node_new) {
+		keep = (!memcmp(neighbor_old->macaddr, neighbor_new->macaddr, sizeof(neighbor_old->macaddr)) &&
+			(neighbor_old->proxy == neighbor_new->proxy) &&
+			(neighbor_old->router == neighbor_new->router));
+	}
+
+	if (node_old) {
+		if (!keep && neighbor_old->enabled)
+			system_del_neighbor(dev, neighbor_old);
+
+		free(neighbor_old);
+	}
+
+	if (node_new) {
+		if (!keep && ip->enabled)
+			if (system_add_neighbor(dev, neighbor_new))
+				neighbor_new->failed = true;
+
+		neighbor_new->enabled = ip->enabled;
+	}
+}
+
+static void
 interface_update_proto_route(struct vlist_tree *tree,
 			     struct vlist_node *node_new,
 			     struct vlist_node *node_old)
@@ -1402,6 +1532,7 @@  void interface_ip_set_enabled(struct interface_ip_settings *ip, bool enabled)
 {
 	struct device_addr *addr;
 	struct device_route *route;
+	struct device_neighbor *neighbor;
 	struct device *dev;
 	struct interface *iface;
 
@@ -1447,7 +1578,6 @@  void interface_ip_set_enabled(struct interface_ip_settings *ip, bool enabled)
 
 		if (!enable_route(ip, route))
 			_enabled = false;
-
 		if (route->enabled == _enabled)
 			continue;
 
@@ -1461,6 +1591,19 @@  void interface_ip_set_enabled(struct interface_ip_settings *ip, bool enabled)
 		route->enabled = _enabled;
 	}
 
+	vlist_for_each_element(&ip->neighbor, neighbor, node) {
+		if (neighbor->enabled == enabled)
+			continue;
+
+		if (enabled) {
+			if(system_add_neighbor(dev, neighbor))
+				neighbor->failed = true;
+		} else
+			system_del_neighbor(dev, neighbor);
+
+		neighbor->enabled = enabled;
+	}
+
 	struct device_prefix *c;
 	struct device_prefix_assignment *a;
 	list_for_each_entry(c, &prefixes, head)
@@ -1489,6 +1632,7 @@  interface_ip_update_start(struct interface_ip_settings *ip)
 	vlist_update(&ip->route);
 	vlist_update(&ip->addr);
 	vlist_update(&ip->prefix);
+	vlist_update(&ip->neighbor);
 }
 
 void
@@ -1499,6 +1643,7 @@  interface_ip_update_complete(struct interface_ip_settings *ip)
 	vlist_flush(&ip->route);
 	vlist_flush(&ip->addr);
 	vlist_flush(&ip->prefix);
+	vlist_flush(&ip->neighbor);
 	interface_write_resolv_conf();
 }
 
@@ -1511,6 +1656,7 @@  interface_ip_flush(struct interface_ip_settings *ip)
 	vlist_simple_flush_all(&ip->dns_search);
 	vlist_flush_all(&ip->route);
 	vlist_flush_all(&ip->addr);
+	vlist_flush_all(&ip->neighbor);
 	vlist_flush_all(&ip->prefix);
 }
 
@@ -1522,6 +1668,7 @@  __interface_ip_init(struct interface_ip_settings *ip, struct interface *iface)
 	vlist_simple_init(&ip->dns_search, struct dns_search_domain, node);
 	vlist_simple_init(&ip->dns_servers, struct dns_server, node);
 	vlist_init(&ip->route, route_cmp, interface_update_proto_route);
+	vlist_init(&ip->neighbor, neighbor_cmp, interface_update_proto_neighbor);
 	vlist_init(&ip->addr, addr_cmp, interface_update_proto_addr);
 	vlist_init(&ip->prefix, prefix_cmp, interface_update_prefix);
 }
diff --git a/interface-ip.h b/interface-ip.h
index 21c6e9b..3f99eb9 100644
--- a/interface-ip.h
+++ b/interface-ip.h
@@ -48,6 +48,9 @@  enum device_addr_flags {
 
 	/* route overrides the default route type */
 	DEVROUTE_TYPE		= (1 << 10),
+
+	/* neighbor mac address */
+	DEVNEIGH_MAC		= (1 << 11),
 };
 
 union if_addr {
@@ -106,6 +109,20 @@  struct device_route {
 	union if_addr source;
 };
 
+struct device_neighbor {
+	struct vlist_node node;
+
+	bool failed;
+	bool proxy;
+	bool keep;
+	bool enabled;
+	bool router;
+
+	uint8_t macaddr[6];
+	enum device_addr_flags flags;
+	union if_addr addr;
+};
+
 struct device_addr {
 	struct vlist_node node;
 	bool enabled;
@@ -150,6 +167,7 @@  struct dns_search_domain {
 };
 
 extern const struct uci_blob_param_list route_attr_list;
+extern const struct uci_blob_param_list neighbor_attr_list;
 extern struct list_head prefixes;
 
 void interface_ip_init(struct interface *iface);
@@ -158,7 +176,7 @@  void interface_add_dns_search_list(struct interface_ip_settings *ip, struct blob
 void interface_write_resolv_conf(void);
 
 void interface_ip_add_route(struct interface *iface, struct blob_attr *attr, bool v6);
-
+void interface_ip_add_neighbor(struct interface *iface, struct blob_attr *attr, bool v6);
 void interface_ip_update_start(struct interface_ip_settings *ip);
 void interface_ip_update_complete(struct interface_ip_settings *ip);
 void interface_ip_flush(struct interface_ip_settings *ip);
diff --git a/interface.h b/interface.h
index 6f10484..3c0c9ed 100644
--- a/interface.h
+++ b/interface.h
@@ -84,6 +84,7 @@  struct interface_ip_settings {
 	struct vlist_tree addr;
 	struct vlist_tree route;
 	struct vlist_tree prefix;
+	struct vlist_tree neighbor;
 
 	struct vlist_simple_tree dns_servers;
 	struct vlist_simple_tree dns_search;
@@ -150,6 +151,7 @@  struct interface {
 	struct interface_ip_settings proto_ip;
 	struct interface_ip_settings config_ip;
 	struct vlist_tree host_routes;
+	struct vlist_tree host_neighbors;
 
 	int metric;
 	int dns_metric;
diff --git a/proto-shell.c b/proto-shell.c
index 47a9568..07ec21a 100644
--- a/proto-shell.c
+++ b/proto-shell.c
@@ -414,6 +414,23 @@  proto_shell_parse_route_list(struct interface *iface, struct blob_attr *attr,
 }
 
 static void
+proto_shell_parse_neighbor_list(struct interface *iface, struct blob_attr *attr,
+				bool v6)
+{
+	struct blob_attr *cur;
+	int rem;
+
+	blobmsg_for_each_attr(cur, attr, rem) {
+		if (blobmsg_type(cur) != BLOBMSG_TYPE_TABLE) {
+			DPRINTF("Ignore wrong neighbor type: %d\n", blobmsg_type(cur));
+			continue;
+		}
+
+		interface_ip_add_neighbor(iface, cur, v6);
+	}
+}
+
+static void
 proto_shell_parse_data(struct interface *iface, struct blob_attr *attr)
 {
 	struct blob_attr *cur;
@@ -456,6 +473,8 @@  enum {
 	NOTIFY_HOST,
 	NOTIFY_DNS,
 	NOTIFY_DNS_SEARCH,
+	NOTIFY_NEIGHBORS,
+	NOTIFY_NEIGHBORS6,
 	__NOTIFY_LAST
 };
 
@@ -477,6 +496,8 @@  static const struct blobmsg_policy notify_attr[__NOTIFY_LAST] = {
 	[NOTIFY_HOST] = { .name = "host", .type = BLOBMSG_TYPE_STRING },
 	[NOTIFY_DNS] = { .name = "dns", .type = BLOBMSG_TYPE_ARRAY },
 	[NOTIFY_DNS_SEARCH] = { .name = "dns_search", .type = BLOBMSG_TYPE_ARRAY },
+	[NOTIFY_NEIGHBORS]= {.name = "neighbor", .type = BLOBMSG_TYPE_ARRAY},
+	[NOTIFY_NEIGHBORS6]= {.name = "neighbor6", .type = BLOBMSG_TYPE_ARRAY},
 };
 
 static int
@@ -546,6 +567,12 @@  proto_shell_update_link(struct proto_shell_state *state, struct blob_attr *data,
 	if ((cur = tb[NOTIFY_ROUTES6]) != NULL)
 		proto_shell_parse_route_list(state->proto.iface, cur, true);
 
+	if ((cur = tb[NOTIFY_NEIGHBORS]) != NULL)
+		proto_shell_parse_neighbor_list(state->proto.iface, cur, false);
+
+	if ((cur = tb[NOTIFY_NEIGHBORS6]) != NULL)
+		proto_shell_parse_neighbor_list(state->proto.iface, cur, true);
+
 	if ((cur = tb[NOTIFY_DNS]))
 		interface_add_dns_server_list(&iface->proto_ip, cur);
 
diff --git a/scripts/netifd-proto.sh b/scripts/netifd-proto.sh
index 31df91f..87d337d 100644
--- a/scripts/netifd-proto.sh
+++ b/scripts/netifd-proto.sh
@@ -64,6 +64,8 @@  proto_init_update() {
 	PROTO_PREFIX6=
 	PROTO_DNS=
 	PROTO_DNS_SEARCH=
+	PROTO_NEIGHBOR=
+	PROTO_NEIGHBOR6=
 	json_init
 	json_add_int action 0
 	[ -n "$ifname" -a "*" != "$ifname" ] && json_add_string "ifname" "$ifname"
@@ -133,6 +135,23 @@  proto_add_ipv6_address() {
 	append PROTO_IP6ADDR "$address/$mask/$preferred/$valid/$offlink/$class"
 }
 
+proto_add_ipv4_neighbor(){
+	local address="$1"
+	local mac="$2"
+	local proxy="$3"
+
+	append PROTO_NEIGHBOR "$address/$mac/$proxy"
+}
+
+proto_add_ipv6_neighbor(){
+	local address="$1"
+	local mac="$2"
+	local proxy="$3"
+	local router="$4"
+
+	append PROTO_NEIGHBOR6 "$address/$mac/$proxy/$router"
+}
+
 proto_add_ipv4_route() {
 	local target="$1"
 	local mask="$2"
@@ -218,6 +237,43 @@  _proto_push_string() {
 	json_add_string "" "$1"
 }
 
+_proto_push_ipv4_neighbor(){
+	local str="$1"
+	local address mac proxy
+
+	address="${str%%/*}"
+	str="${str#*/}"
+	mac="${str%%/*}"
+	str="${str#*/}"
+	proxy="${str%%/*}"
+
+	json_add_object ""
+	json_add_string ipaddr "$address"
+	[ -n "$mac" ] && json_add_string mac "$mac"
+	[ -n "$proxy" ] && json_add_boolean proxy "$proxy"
+	json_close_object
+}
+
+_proto_push_ipv6_neighbor(){
+	local str="$1"
+	local address mac proxy router
+
+	address="${str%%/*}"
+	str="${str#*/}"
+	mac="${str%%/*}"
+	str="${str#*/}"
+	proxy="${str%%/*}"
+	str="${str#*/}"
+	router="${str%%/*}"
+
+	json_add_object ""
+	json_add_string ipaddr "$address"
+	[ -n "$mac" ] && json_add_string mac "$mac"
+	[ -n "$proxy" ] && json_add_boolean proxy "$proxy"
+	[ -n "$router" ] && json_add_boolean router "$router"
+	json_close_object
+}
+
 _proto_push_route() {
 	local str="$1";
 	local target="${str%%/*}"
@@ -277,6 +333,8 @@  proto_send_update() {
 	_proto_push_array "ip6prefix" "$PROTO_PREFIX6" _proto_push_string
 	_proto_push_array "dns" "$PROTO_DNS" _proto_push_string
 	_proto_push_array "dns_search" "$PROTO_DNS_SEARCH" _proto_push_string
+	_proto_push_array "neighbor" "$PROTO_NEIGHBOR" _proto_push_ipv4_neighbor
+	_proto_push_array "neighbor6" "$PROTO_NEIGHBOR6" _proto_push_ipv6_neighbor
 	_proto_notify "$interface"
 }
 
diff --git a/system-dummy.c b/system-dummy.c
index 11c8ccc..58fd2d0 100644
--- a/system-dummy.c
+++ b/system-dummy.c
@@ -181,6 +181,26 @@  static int system_route_msg(struct device *dev, struct device_route *route, cons
 	return 0;
 }
 
+static int system_neighbor_msg(struct device *dev, struct device_neighbor *neighbor, const char *type)
+{
+	char addr[64];
+	int af = system_get_addr_family(neighbor->flags);
+	inet_ntop(af, &neighbor->addr.in , addr, sizeof(addr));
+
+	D(SYSTEM, "neigh %s %s%s%s %s\n", type, addr, neighbor->proxy ? "proxy " : "",
+		(neighbor->flags & DEVNEIGH_MAC) ? format_macaddr(neighbor->macaddr) : "",
+		neighbor->router ? "router": "");
+}
+int system_add_neighbor(struct device *dev, struct device_neighbor *neighbor)
+{
+	return system_neighbor_msg(dev, neighbor, "add");
+}
+
+int system_del_neighbor(struct device *dev, struct device_neighbor *neighbor)
+{
+	return system_neighbor_msg(dev, neighbor, "del");
+}
+
 int system_add_route(struct device *dev, struct device_route *route)
 {
 	return system_route_msg(dev, route, "add");
diff --git a/system-linux.c b/system-linux.c
index 82e9928..6a0105e 100644
--- a/system-linux.c
+++ b/system-linux.c
@@ -31,6 +31,7 @@ 
 #include <netinet/in.h>
 
 #include <linux/rtnetlink.h>
+#include <linux/neighbour.h>
 #include <linux/sockios.h>
 #include <linux/ip.h>
 #include <linux/if_addr.h>
@@ -1023,8 +1024,8 @@  void system_if_clear_state(struct device *dev)
 {
 	static char buf[256];
 	char *bridge;
-
 	device_set_ifindex(dev, system_if_resolve(dev));
+
 	if (dev->external || !dev->ifindex)
 		return;
 
@@ -1046,6 +1047,8 @@  void system_if_clear_state(struct device *dev)
 	system_if_clear_entries(dev, RTM_GETADDR, AF_INET);
 	system_if_clear_entries(dev, RTM_GETROUTE, AF_INET6);
 	system_if_clear_entries(dev, RTM_GETADDR, AF_INET6);
+	system_if_clear_entries(dev, RTM_GETNEIGH, AF_INET);
+	system_if_clear_entries(dev, RTM_GETNEIGH, AF_INET6);
 	system_set_disable_ipv6(dev, "0");
 }
 
@@ -1930,6 +1933,51 @@  int system_del_address(struct device *dev, struct device_addr *addr)
 	return system_addr(dev, addr, RTM_DELADDR);
 }
 
+static int system_neigh(struct device *dev, struct device_neighbor *neighbor, int cmd)
+{
+	int alen = ((neighbor->flags & DEVADDR_FAMILY) == DEVADDR_INET4) ? 4 : 16;
+	unsigned int flags = 0;
+	struct ndmsg ndm = {
+		.ndm_family = (alen == 4) ? AF_INET : AF_INET6,
+		.ndm_ifindex = dev->ifindex,
+		.ndm_state = NUD_PERMANENT,
+		.ndm_flags = (neighbor->proxy ? NTF_PROXY : 0) | (neighbor->router ? NTF_ROUTER : 0),
+	};
+	struct nl_msg *msg;
+
+	if (!dev)
+		return 1;
+
+	if (cmd == RTM_NEWNEIGH)
+		flags |= NLM_F_CREATE | NLM_F_REPLACE;
+
+	msg = nlmsg_alloc_simple(cmd, flags);
+
+	if (!msg)
+		return -1;
+
+	nlmsg_append(msg, &ndm, sizeof(ndm), 0);
+
+	nla_put(msg, NDA_DST, alen, &neighbor->addr);
+	if (neighbor->flags & DEVNEIGH_MAC)
+		nla_put(msg, NDA_LLADDR, sizeof(neighbor->macaddr), &neighbor->macaddr);
+
+
+	return system_rtnl_call(msg);
+}
+
+int system_add_neighbor(struct device *dev, struct device_neighbor *neighbor)
+{
+	return system_neigh(dev, neighbor, RTM_NEWNEIGH);
+}
+
+int system_del_neighbor(struct device *dev, struct device_neighbor *neighbor)
+{
+	int rval = system_neigh(dev, neighbor, RTM_DELNEIGH);
+	netifd_log_message(L_NOTICE,"return delete %d", rval);
+	return rval;
+}
+
 static int system_rt(struct device *dev, struct device_route *route, int cmd)
 {
 	int alen = ((route->flags & DEVADDR_FAMILY) == DEVADDR_INET4) ? 4 : 16;
diff --git a/system.h b/system.h
index 4d4cf6e..9fefcae 100644
--- a/system.h
+++ b/system.h
@@ -213,6 +213,9 @@  int system_add_route(struct device *dev, struct device_route *route);
 int system_del_route(struct device *dev, struct device_route *route);
 int system_flush_routes(void);
 
+int system_add_neighbor(struct device *dev, struct device_neighbor * neighbor);
+int system_del_neighbor(struct device *dev, struct device_neighbor * neighbor);
+
 bool system_resolve_rt_type(const char *type, unsigned int *id);
 bool system_resolve_rt_proto(const char *type, unsigned int *id);
 bool system_resolve_rt_table(const char *name, unsigned int *id);
diff --git a/ubus.c b/ubus.c
index 32bc1a3..0e86b61 100644
--- a/ubus.c
+++ b/ubus.c
@@ -459,6 +459,43 @@  interface_ip_dump_address_list(struct interface_ip_settings *ip, bool v6, bool e
 }
 
 static void
+interface_ip_dump_neighbor_list(struct interface_ip_settings *ip, bool enabled)
+{
+	struct device_neighbor *neighbor;
+	int buflen = 128;
+	char *buf;
+	void *r;
+	int af;
+
+	vlist_for_each_element(&ip->neighbor, neighbor, node) {
+		if (neighbor->enabled != enabled)
+			continue;
+
+		if ((neighbor->flags & DEVADDR_FAMILY) == DEVADDR_INET4)
+			af = AF_INET;
+		else
+			af = AF_INET6;
+
+		r = blobmsg_open_table(&b, NULL);
+
+		if (neighbor->flags & DEVNEIGH_MAC)
+			blobmsg_add_string(&b, "mac", format_macaddr(neighbor->macaddr));
+
+		buf = blobmsg_alloc_string_buffer(&b , "address", buflen);
+		inet_ntop(af, &neighbor->addr, buf, buflen);
+		blobmsg_add_string_buffer(&b);
+
+		if (neighbor->proxy)
+			blobmsg_add_u32(&b, "proxy", neighbor->proxy);
+
+		if (neighbor->router)
+			blobmsg_add_u32(&b, "router", neighbor->router);
+
+		blobmsg_close_table(&b, r);
+	}
+}
+
+static void
 interface_ip_dump_route_list(struct interface_ip_settings *ip, bool enabled)
 {
 	struct device_route *route;
@@ -737,6 +774,10 @@  netifd_dump_status(struct interface *iface)
 		interface_ip_dump_dns_search_list(&iface->config_ip, true);
 		interface_ip_dump_dns_search_list(&iface->proto_ip, true);
 		blobmsg_close_array(&b, a);
+		a = blobmsg_open_array(&b, "neighbors");
+		interface_ip_dump_neighbor_list(&iface->config_ip, true);
+		interface_ip_dump_neighbor_list(&iface->proto_ip, true);
+		blobmsg_close_array(&b, a);
 
 		inactive = blobmsg_open_table(&b, "inactive");
 		a = blobmsg_open_array(&b, "ipv4-address");
@@ -759,6 +800,10 @@  netifd_dump_status(struct interface *iface)
 		interface_ip_dump_dns_search_list(&iface->config_ip, false);
 		interface_ip_dump_dns_search_list(&iface->proto_ip, false);
 		blobmsg_close_array(&b, a);
+		a = blobmsg_open_array(&b, "neighbors");
+		interface_ip_dump_neighbor_list(&iface->config_ip, false);
+		interface_ip_dump_neighbor_list(&iface->proto_ip, false);
+		blobmsg_close_array(&b, a);
 		blobmsg_close_table(&b, inactive);
 	}