Skip to content
Snippets Groups Projects
hostmngr.c 21.5 KiB
Newer Older
  • Learn to ignore specific revisions
  • Anjan Chanda's avatar
    Anjan Chanda committed
    /*
    
     * hostmngr.c - implementation file for network hosts management.
    
    Anjan Chanda's avatar
    Anjan Chanda committed
     *
     * Copyright (C) 2023 IOPSYS Software Solutions AB. All rights reserved.
     *
     * See LICENSE file for license related information.
     *
     */
    
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <stdint.h>
    #include <errno.h>
    
    #include <unistd.h>
    #include <fcntl.h>
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <sys/ioctl.h>
    #include <net/if_arp.h>
    #include <net/if.h>
    #include <arpa/inet.h>
    
    #include <linux/if_packet.h>
    #include <net/ethernet.h>
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    
    #include <netlink/route/link.h>
    
    #include <json-c/json.h>
    #include <libubox/blobmsg.h>
    #include <libubox/blobmsg_json.h>
    #include <libubox/uloop.h>
    #include <libubox/ustream.h>
    #include <libubox/utils.h>
    #include <libubus.h>
    
    #include <easy/easy.h>
    
    
    #include "wifi_api.h"
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    #include "util.h"
    
    #include "useropts.h"
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    #include "debug.h"
    #include "timer.h"
    #include "neigh.h"
    
    #include "hostmngr.h"
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    
    
    static int signal_pending;
    
    
    static void hostmngr_sighandler(int sig)
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    {
    	signal_pending = sig;
    }
    
    
    static void hostmngr_update_interface_bridgeinfo(struct local_interface *iface)
    {
    
    	iface->is_bridge = if_isbridge(iface->ifname);
    	if (!iface->is_bridge) {
    		int br_ifindex;
    
    		br_ifindex = if_isbridge_interface(iface->ifname);
    		if (br_ifindex > 0) {
    			iface->is_brif = true;
    			iface->brport = if_brportnum(iface->ifname);
    			iface->br_ifindex = br_ifindex;
    		}
    	}
    
    }
    
    static void hostmngr_update_interface_ipaddrs(struct local_interface *iface)
    {
    	struct ip_address ips[32] = {0};
    	int num_ipaddrs = 32;
    	int ret;
    
    
    	ret = if_getaddrs(iface->ifname, ips, &num_ipaddrs);
    	if (!ret && num_ipaddrs > 0) {
    		iface->ipaddrs = calloc(num_ipaddrs, sizeof(struct ip_address));
    		if (iface->ipaddrs) {
    			iface->num_ipaddrs = num_ipaddrs;
    			memcpy(iface->ipaddrs, ips, num_ipaddrs * sizeof(struct ip_address));
    		}
    	}
    
    static void hostmngr_update_interface_mediatype(struct local_interface *iface)
    {
    
    	if (strncmp(iface->ifname, "lo", 2)) {
    		if (is_wifi_interface(iface->ifname)) {
    			iface->mediatype = IF_MEDIA_WIFI;
    
    			if (is_ap_interface(iface->ifname)) {
    
    				iface->is_ap = true;
    
    				iface->is_affiliated_ap = is_affiliated_ap(iface->ifname);
    				if (iface->is_affiliated_ap)
    					ap_get_mld_ifname(iface->ifname, iface->mld_ifname);
    			}
    
    		} else {
    			iface->mediatype = IF_MEDIA_ETH;
    		}
    	}
    
    }
    
    struct local_interface *hostmngr_alloc_interface(const char *ifname)
    {
    	struct local_interface *iface = NULL;
    
    	iface = calloc(1, sizeof(*iface));
    	if (!iface) {
    		return NULL;
    	}
    
    	snprintf(iface->ifname, 16, "%s", ifname);
    	iface->ifindex = if_nametoindex(ifname);
    	dbg("%s: %s (%d)\n", __func__, iface->ifname, iface->ifindex);
    	if_gethwaddr(ifname, iface->macaddr);
    	if_getflags(ifname, &iface->ifflags);
    	if_getoperstate(ifname, &iface->operstate);
    	if_getcarrier(ifname, &iface->carrier);
    
    	hostmngr_update_interface_bridgeinfo(iface);
    	hostmngr_update_interface_ipaddrs(iface);
    	hostmngr_update_interface_mediatype(iface);
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    void enum_interfaces_cb(struct nl_object *obj, void *arg)
    {
    	struct enum_interfaces_arg {
    		struct list_head *iflist;
    		int *n;
    	} *argp = arg;
    	struct ip_address ips[32] = {0};
    
    	struct local_interface *iface = NULL;
    	struct rtnl_link *link = NULL;
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    	int num_ipaddrs = 32;
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    
    
    	nl_object_get(obj);
    	link = (struct rtnl_link *)obj;
    
    	if (rtnl_link_get_arptype(link) == ARPHRD_VOID)
    		goto out;
    
    	iface = calloc(1, sizeof(*iface));
    	if (!iface) {
    		*argp->n = 0;
    		return;
    	}
    
    	strncpy(iface->ifname, rtnl_link_get_name(link), 16);
    	iface->ifindex = rtnl_link_get_ifindex(link);
    
    	iface->exclude = 1;
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    
    	a = rtnl_link_get_addr(link);
    
    	if (a && nl_addr_get_len(a) == 6)
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    		memcpy(iface->macaddr, nl_addr_get_binary_addr(a), nl_addr_get_len(a));
    
    	iface->ifflags = rtnl_link_get_flags(link);
    	iface->operstate = rtnl_link_get_operstate(link);
    
    
    	hostmngr_update_interface_bridgeinfo(iface);
    	hostmngr_update_interface_ipaddrs(iface);
    	hostmngr_update_interface_mediatype(iface);
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    
    	list_add_tail(&iface->list, argp->iflist);
    	*argp->n += 1;
    
    out:
    	nl_object_put((struct nl_object *)link);
    }
    
    int enum_interfaces(struct list_head *head, int *num)
    {
    	struct nl_cache *cache;
    	struct nl_sock *sk;
    	int ret = 0;
    	struct enum_interfaces_arg {
    		struct list_head *head;
    		int *n;
    	} arg = {
    		.head = head,
    		.n = num,
    	};
    
    	sk = nl_socket_alloc();
    	if (sk == NULL) {
    		ret = -errno;
    		return ret;
    	}
    
    	nl_connect(sk, NETLINK_ROUTE);
    
    	ret = rtnl_link_alloc_cache(sk, AF_UNSPEC, &cache);
    	if (ret) {
    		nl_socket_free(sk);
    		ret = -errno;
    		return -1;
    	}
    
    	nl_cache_foreach(cache, enum_interfaces_cb, &arg);
    	nl_cache_put(cache);
    	nl_socket_free(sk);
    	return 0;
    }
    
    
    int hostmngr_enumerate_interfaces(struct hostmngr_private *priv)
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    {
    	return enum_interfaces(&priv->iflist, &priv->num_local_interfaces);
    }
    
    
    static void hostmngr_init_interface_private(struct hostmngr_private *priv)
    
    {
    	struct local_interface *e;
    
    	if (priv->num_local_interfaces == 0 || list_empty(&priv->iflist))
    		return;
    
    	list_for_each_entry(e, &priv->iflist, list) {
    		e->priv = priv;
    
    		if (e->exclude)
    
    		if (e->is_bridge)
    			hostmngr_register_arp_sock(e->ifname, &e->arpsk, e);
    
    void hostmngr_print_interfaces(struct hostmngr_private *priv)
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    {
    	struct local_interface *e;
    	int i = 0;
    
    	if (priv->num_local_interfaces == 0 || list_empty(&priv->iflist))
    		return;
    
    	list_for_each_entry(e, &priv->iflist, list) {
    
    		dbg("%d: ifname = %16s, mac = " MACFMT ", ifindex = %3d, flags = 0x%08x, opstate = 0x%02x, is-bridge = %d, brport = %d\n",
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    			i++, e->ifname, MAC2STR(e->macaddr), e->ifindex,
    
    			e->ifflags, e->operstate, e->is_bridge, e->brport);
    
    Jakob Olsson's avatar
    Jakob Olsson committed
    void hostmngr_free_interfaces(struct hostmngr_private *priv)
    {
    	struct local_interface *e = NULL, *tmp;
    	int i = 0;
    
    	if (priv->num_local_interfaces == 0 || list_empty(&priv->iflist))
    		return;
    
    	list_for_each_entry_safe(e, tmp, &priv->iflist, list) {
    		if (e->mediainfo)
    			free(e->mediainfo);
    		if (e->num_ipaddrs && e->ipaddrs)
    			free(e->ipaddrs);
    		list_del(&e->list);
    		free(e);
    	}
    }
    
    
    int hostmngr_enumerate_interfaces_in_zone(struct hostmngr_private *priv,
    
    					  const char *zone, char *iflist,
    					  int *num)
    {
    
    }
    #endif
    
    
    
    int hostmngr_include_interface(struct hostmngr_private *priv, const char *ifname)
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    {
    	struct local_interface *e;
    
    
    	if (if_isbridge(ifname)) {
    		char ifnames[32][16] = {0};
    		int n = 32;
    		int ret;
    		int i;
    
    		ret = br_get_iflist(ifname, &n, ifnames);
    		if (ret)
    			return -1;
    
    		list_for_each_entry(e, &priv->iflist, list) {
    			for (i = 0; i < n; i++) {
    				if (!strncmp(e->ifname, ifnames[i], strlen(e->ifname))) {
    					e->exclude = 0;
    					dbg("Include interface '%s'\n", e->ifname);
    					break;
    				}
    			}
    		}
    	}
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    
    	list_for_each_entry(e, &priv->iflist, list) {
    
    		if (!strncmp(e->ifname, ifname, strlen(e->ifname))) {
    			e->exclude = 0;
    			dbg("Include interface '%s'\n", e->ifname);
    			break;
    		}
    	}
    
    	return 0;
    }
    
    
    int hostmngr_exclude_interfaces(struct hostmngr_private *priv, void *useropts)
    
    	struct hostmngr_useropts *uopts = (struct hostmngr_useropts *)useropts;
    
    	struct local_interface *e;
    	int i;
    
    	/* exclude interface(s) passed in cmdline */
    	if (!uopts || uopts->num_exclude == 0)
    		return 0;
    
    	list_for_each_entry(e, &priv->iflist, list) {
    		for (i = 0; i < uopts->num_exclude; i++) {
    			if (!strncmp(e->ifname, uopts->exclude_ifnames[i],
    				     strlen(e->ifname))) {
    
    				e->exclude = 1;
    				break;
    			}
    		}
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    	}
    
    	return 0;
    }
    
    
    int hostmngr_get_interface_neighbors(struct hostmngr_private *priv)
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    {
    	struct local_interface *e;
    
    	if (priv->num_local_interfaces == 0 || list_empty(&priv->iflist))
    		return 0;
    
    
    	list_for_each_entry(e, &priv->iflist, list) {
    		if (e->exclude || hwaddr_is_zero(e->macaddr))
    			continue;
    
    		if (e->is_ap)
    			hostmngr_get_wifi_stations(priv, e);
    	}
    
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    	list_for_each_entry(e, &priv->iflist, list) {
    		if (e->exclude || hwaddr_is_zero(e->macaddr))
    			continue;
    
    
    		hostmngr_get_known_neighbors(priv, e->ifname);
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    	}
    
    	return 0;
    }
    
    void remove_newline(char *buf)
    {
    	int len = strlen(buf) - 1;
    
    	if (buf[len] == '\n')
    		buf[len] = 0;
    }
    
    int read_dhcpv4_lease_table(void **clients, int *num_clients)
    {
    	struct dhcp_clients {
    		uint8_t macaddr[6];
    		char ipv4[24];
    		char hostname[256];
    		unsigned long leasetime;
    
    	} *hosts = NULL, *tmp = NULL, *ptr;
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    	FILE *f = NULL;
    	char line[256];
    	int cnt = 0;
    	int ret = 0;
    
    	f = fopen("/var/dhcp.leases", "r");
    	if (!f)
    		return -1;
    
    	while (fgets(line, sizeof(line), f) != NULL) {
    
    		unsigned long leasetime = 0;
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    		char macaddr[18] = {0};
    		char ipaddr[24] = {0};
    		char hostname[256] = {0};
    		char clid[256] = {0};
    
    		remove_newline(line);
    
    		if (sscanf(line, "%lu %17s %23s %255s %255s", &leasetime,
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    			   macaddr, ipaddr, hostname, clid) == 5) {
    
    
    			ptr = realloc(hosts, (cnt + 1) * sizeof(struct dhcp_clients));
    			if (!ptr) {
    				if (hosts)
    					free(hosts);
    
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    				*num_clients = 0;
    				ret = -1;
    				goto out;
    			}
    
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    			tmp = hosts + cnt;
    			memset(tmp, 0, sizeof(struct dhcp_clients));
    			hwaddr_aton(macaddr, tmp->macaddr);
    			strncpy(tmp->ipv4, ipaddr, sizeof(ipaddr)-1);
    			strncpy(tmp->hostname, hostname, sizeof(hostname)-1);
    			tmp->leasetime = leasetime;
    			cnt += 1;
    		}
    	}
    
    	*clients = hosts;
    	*num_clients = cnt;
    
    	dbg("%s: num-dhcphosts = %d\n", __func__, *num_clients);
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    out:
    	fclose(f);
    	return ret;
    }
    
    
    void hostmngr_update_hostname_of_neighbors(struct hostmngr_private *priv,
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    					   void *dhclients, int num)
    {
    	struct dhcp_clients {
    		uint8_t macaddr[6];
    		char ipv4[24];
    		char hostname[256];
    		unsigned long leasetime;
    	} *h, *hosts = (struct dhcp_clients *)dhclients;
    
    	struct neigh_entry *e = NULL;
    	struct neigh_ip_entry *ipn;
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    	int i;
    
    
    	if (num == 0 || !hosts)
    		return;
    
    	for (i = 0; i < num; i++) {
    		h = hosts + i;
    		e = neigh_lookup(&priv->neigh_q, h->macaddr);
    		if (e) {
    			strncpy(e->hostname, h->hostname, sizeof(e->hostname) - 1);
    			inet_aton(h->ipv4, &e->ipv4.addr.ip4);
    			e->ipv4.family = AF_INET;
    			e->leasetime = h->leasetime;
    
    			e->ipv4_type_dhcp = 1;
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    
    
    			if (!neigh_is_ip_known(e, &e->ipv4)) {
    				struct ip_address_entry *new;
    				char newip[64] = {0};
    
    				inet_ntop(e->ipv4.family, &e->ipv4.addr, newip, sizeof(newip));
    				dbg("%s: Adding new ipaddress %s to entry " MACFMT"\n",
    					__func__, newip, MAC2STR(e->macaddr));
    
    				new = calloc(1, sizeof(*new));
    				if (new) {
    					memcpy(&new->ip, &e->ipv4, sizeof(e->ipv4));
    					list_add_tail(&new->list, &e->iplist);
    				}
    			}
    
    
    			ipn = neigh_ip_entry_lookup2(priv, &e->ipv4);
    			if (ipn) {
    				ipn->neigh = e;
    				memcpy(ipn->macaddr, e->macaddr, 6);
    				dbg("Host " MACFMT " with ip = %s (0x%x) added to iptable\n",
    				    MAC2STR(e->macaddr), h->ipv4, e->ipv4.addr.ip4.s_addr);
    
    int hostmngr_get_neigh_hostname(struct neigh_queue *q, uint8_t *macaddr)
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    {
    
    	struct hostmngr_private *priv = container_of(q, struct hostmngr_private, neigh_q);	//TODO:
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    	struct dhcp_clients {
    		uint8_t macaddr[6];
    		char ipv4[24];
    		char hostname[256];
    		unsigned long leasetime;
    	} *h, *hosts;
    
    	struct neigh_entry *e = NULL;
    	struct neigh_ip_entry *ipn;
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    	int i;
    
    	void *entries = NULL;
    	int num = 0;
    	int ret;
    
    	e = neigh_lookup(q, macaddr);
    	if (!e)
    		return -1;
    
    	ret = read_dhcpv4_lease_table(&entries, &num);
    
    	if (ret || num <= 0) {
    		ret = -1;
    		goto out;
    	}
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    
    
    	dbg("%s: num-dhcphosts = %d\n", __func__, num);
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    	hosts = (struct dhcp_clients *)entries;
    	for (i = 0; i < num; i++) {
    		h = hosts + i;
    		if (hwaddr_equal(h->macaddr, macaddr)) {
    			strncpy(e->hostname, h->hostname, sizeof(e->hostname) - 1);
    			inet_aton(h->ipv4, &e->ipv4.addr.ip4);
    			e->ipv4.family = AF_INET;
    			e->leasetime = h->leasetime;
    
    			e->ipv4_type_dhcp = 1;
    
    			e->ipv4_type_static = 0;
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    
    
    			if (!neigh_is_ip_known(e, &e->ipv4)) {
    				struct ip_address_entry *new;
    				char newip[64] = {0};
    
    				inet_ntop(e->ipv4.family, &e->ipv4.addr, newip, sizeof(newip));
    				dbg("%s: Adding new ipaddress %s to entry " MACFMT"\n",
    					__func__, newip, MAC2STR(e->macaddr));
    
    				new = calloc(1, sizeof(*new));
    				if (new) {
    					memcpy(&new->ip, &e->ipv4, sizeof(e->ipv4));
    					list_add_tail(&new->list, &e->iplist);
    				}
    			}
    
    
    
    			ipn = neigh_ip_entry_lookup2(priv, &e->ipv4);
    			if (ipn) {
    				ipn->neigh = e;
    				memcpy(ipn->macaddr, e->macaddr, 6);
    				dbg("Host " MACFMT " with ip = %s (0x%x) added to iptable\n",
    				    MAC2STR(e->macaddr), h->ipv4, e->ipv4.addr.ip4.s_addr);
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    			}
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    			ret = 0;
    			break;
    		}
    	}
    
    
    
    out:
    	/* if lease table has been consulted before with no results
    	 * assume static IP address
    	 */
    	if (e->event_pending && !strlen(e->hostname)) {
    		e->ipv4_type_static = 1;
    		e->ipv4_type_dhcp = 0;
    	}
    
    	if (entries)
    		free(entries);
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    	return ret;
    }
    
    
    int hostmngr_get_neighbors_hostname(struct hostmngr_private *priv)
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    {
    	void *entries = NULL;
    	int num = 0;
    	int ret;
    
    	ret = read_dhcpv4_lease_table(&entries, &num);
    	if (!ret && num > 0) {
    
    		hostmngr_update_hostname_of_neighbors(priv, entries, num);
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    		free(entries);
    	}
    
    	return 0;
    }
    
    
    int hostmngr_register_notifier_file(struct hostmngr_private *priv, const char *filename)
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    {
    #if 0	//TODO
    	int wd;
    
    	priv->fd_dhcp4 = inotify_init1(IN_NONBLOCK);
    	if (priv->fd_dhcp4 == -1) {
    
    		dbg("Error: %s\n", strerror(errno));
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    		return -1;
    	}
    
    	wd = inotify_add_watch(fd, filename, IN_MODIFY);
    	if (wd == -1) {
    		close(priv->fd_dhcp4);
    		return -1;
    	}
    
    	//TODO; uloop_fd_add() etc.
    #endif
    	return 0;
    }
    
    
    static void recv_arp_cb(struct uloop_fd *u, unsigned int events)
    {
    	struct arp_sock *sk = container_of(u, struct arp_sock, uloop);
    	struct local_interface *iface = (struct local_interface *)sk->priv;
    
    	if (u->error) {
    
    Jakob Olsson's avatar
    Jakob Olsson committed
    		//int ret = -1;
    		//socklen_t ret_len = sizeof(ret);
    
    
    		dbg("%s: - error -\n", __func__);
    
    Jakob Olsson's avatar
    Jakob Olsson committed
    		u->error = false;
    		/*
    		if (e->error_cb &&
    		    getsockopt(u->fd, SOL_SOCKET, SO_ERROR, &ret, &ret_len) == 0) {
    		       e->error_cb(e, ret);
    		}
    		*/
    
    	}
    
    	for (;;) {
    		char buf[512] = {0};
    		ssize_t len;
    		struct ethhdr *eh;
    
    		len = read(u->fd, buf, sizeof(buf));
    		if (len == -1) {
    			//dbg("%s: len = %d (%s)\n", __func__, len, strerror(errno));
    			return;
    		}
    
    		if (len <= sizeof(struct ethhdr)) {
    			return;
    		}
    
    		eh = (struct ethhdr *)buf;
    		if (ntohs(eh->h_proto) == ETH_P_ARP) {
    
    			struct hostmngr_private *priv = (struct hostmngr_private *)iface->priv;
    
    			err("ARP received from " MACFMT" on %s\n", MAC2STR(eh->h_source), iface->ifname);
    
    			if (!iface->is_brif || !iface->nomaster)
    				neigh_mark_reachable(&priv->neigh_q, eh->h_source, iface->ifname);
    
    int hostmngr_register_arp_sock(const char *ifname, struct arp_sock *arpsk, void *priv)
    
    {
    	struct sockaddr_ll sa;
    	int ifindex;
    	int flags;
    	int s;
    
    	if (!ifname || !arpsk)
    		return -1;
    
    	s = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ARP));
    	if (s < 0)
    		return -1;
    
    	flags = fcntl(s, F_GETFL, 0);
    	if (flags != -1)
    		fcntl(s, F_SETFL, flags | O_NONBLOCK);
    
    	ifindex = if_nametoindex(ifname);
    	if (!ifindex) {
    		close(s);
    		return -1;
    	}
    
    	memset(&sa, 0, sizeof(struct sockaddr_ll));
    	sa.sll_family = AF_PACKET;
    	sa.sll_protocol = htons(ETH_P_ARP);
    	sa.sll_ifindex = ifindex;
    
    	if (bind(s, (struct sockaddr *)&sa, sizeof(struct sockaddr_ll)) < 0) {
    		err("Failed to bind arp sock\n");
    		close(s);
    		return -1;
    	}
    
    	arpsk->uloop.fd = s;
    	arpsk->uloop.cb = recv_arp_cb;
    	uloop_fd_add(&arpsk->uloop, ULOOP_READ);
    	arpsk->priv = priv;
    
    	dbg("%s: %s\n", __func__, ifname);
    
    void hostmngr_unregister_arp_sock(struct arp_sock *arpsk)
    
    {
    	if (arpsk) {
    		uloop_fd_delete(&arpsk->uloop);
    		close(arpsk->uloop.fd);
    		arpsk->priv = NULL;
    	}
    }
    
    
    char *hostmngr_brport_to_ifname(struct hostmngr_private *priv, uint16_t brport)
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    {
    	struct local_interface *iface;
    
    	if (list_empty(&priv->iflist))
    		return NULL;
    
    	list_for_each_entry(iface, &priv->iflist, list) {
    		if (iface->is_brif && iface->brport == brport)
    			return iface->ifname;
    	}
    
    	return NULL;
    }
    
    
    struct local_interface *hostmngr_get_master_interface(struct hostmngr_private *priv,
    
    						      int ifindex)
    {
    	struct local_interface *iface;
    
    	if (list_empty(&priv->iflist))
    		return NULL;
    
    	list_for_each_entry(iface, &priv->iflist, list) {
    		if (iface->is_bridge && iface->ifindex == ifindex)
    			return iface;
    	}
    
    	return NULL;
    }
    
    
    struct local_interface *hostmngr_ifname_to_interface(struct hostmngr_private *priv,
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    						      const char *ifname)
    {
    	struct local_interface *iface;
    
    
    	if (!ifname || !priv)
    		return NULL;
    
    	if (list_empty(&priv->iflist))
    		return NULL;
    
    	list_for_each_entry(iface, &priv->iflist, list) {
    		if (!strncmp(iface->ifname, ifname, 16))
    			return iface;
    	}
    
    	return NULL;
    }
    
    
    char *hostmngr_ifname_to_network(struct hostmngr_private *priv, const char *ifname)
    
    {
    	struct local_interface *iface, *mif;
    
    	if (!ifname || !priv || list_empty(&priv->iflist))
    		return NULL;
    
    	list_for_each_entry(iface, &priv->iflist, list) {
    		if (strncmp(iface->ifname, ifname, 16))
    			continue;
    
    		if (iface->is_brif && iface->br_ifindex > 0) {
    			char master[16] = {0};
    
    			if_indextoname(iface->br_ifindex, master);
    
    			mif = hostmngr_ifname_to_interface(priv, master);
    
    			if (mif)
    				return mif->network;
    		}
    
    		return iface->network;
    	}
    
    	return NULL;
    }
    
    
    int hostmngr_handle_ethport_carrier_off(struct hostmngr_private *priv,
    
    					const char *ifname)
    {
    	if (!priv || !ifname)
    		return -1;
    
    
    	neigh_set_unreachable(&priv->neigh_q, ifname);
    
    int hostmngr_handle_ethport_carrier_on(struct hostmngr_private *priv,
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    {
    	if (!priv || !ifname)
    		return -1;
    
    
    	neigh_probe_unreachable(&priv->neigh_q, ifname);
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    	return 0;
    }
    
    void heartbeat_timer_cb(atimer_t *t)
    {
    
    	struct hostmngr_private *p = container_of(t, struct hostmngr_private, hbtimer);
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    
    	UNUSED(p);
    	switch (signal_pending) {
    	case SIGUSR2:
    		signal_pending = 0;
    		err("%s", "Received SIGUSR2\n");
    		break;
    	default:
    		break;
    	}
    
    	timer_set(t, 1000);
    }
    
    
    static void hostmngr_periodic_refresh(atimer_t *t)
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    {
    
    	struct hostmngr_private *p = container_of(t, struct hostmngr_private, refreshtimer);
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    
    
    	hostmngr_get_interface_network(p);
    
    	hostmngr_get_1905_aladdr(p);
    
    	hostmngr_get_1905_topology(p);
    
    	timer_set(t, 11000);
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    }
    
    
    int hostmngr_init_private(struct hostmngr_private *priv, void *useropts)
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    {
    	int ret;
    
    
    #if 0
    	priv->use_ieee1905 = false;
    	priv->i1905 = lookup_object(priv->bus, IEEE1905_OBJECT);
    	if (priv->i1905 != OBJECT_INVALID) {
    		dbg("%s: Successfully got %s object (id = %u)\n",
    			__func__, IEEE1905_OBJECT, priv->i1905);
    		priv->i1905_topology = lookup_object(priv->bus, IEEE1905_TOPOLOGY_OBJECT);
    		if (priv->i1905_topology != OBJECT_INVALID) {
    			dbg("%s: Successfully got %s object (id = %u)\n",
    				__func__, IEEE1905_TOPOLOGY_OBJECT, priv->i1905_topology);
    			priv->use_ieee1905 = true;
    		}
    	} else {
    		dbg("%s: Failed to get %s object\n", __func__, IEEE1905_OBJECT);
    	}
    #endif
    
    	INIT_LIST_HEAD(&priv->hostlist);
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    	INIT_LIST_HEAD(&priv->iflist);
    
    	ret = hostmngr_enumerate_interfaces(priv);
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    	if (!ret)
    
    		hostmngr_print_interfaces(priv);
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    
    
    	hostmngr_include_interface(priv, DEFAULT_LAN_IFNAME);
    	hostmngr_exclude_interfaces(priv, useropts);
    	hostmngr_init_interface_private(priv);
    
    	hostmngr_get_interface_network(priv);
    
    	hostmngr_get_1905_aladdr(priv);
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    
    	neigh_queue_init(&priv->neigh_q);
    
    
    	if (priv->cfg.history && priv->cfg.history_persist) {
    
    		neigh_history_load_from_json_file(priv, priv->cfg.history_file);
    
    		hosts_load_from_json_file(priv, priv->cfg.hosts_file);
    	}
    
    	hostmngr_get_interface_neighbors(priv);
    	hostmngr_get_neighbors_hostname(priv);
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    
    
    	hostmngr_register_notifier_file(priv, "/var/dhcp.leases");
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    
    	nfct_get_entries_nolo(priv);
    
    	return ret;
    }
    
    
    void hostmngr_exit_private(struct hostmngr_private *priv)
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    {
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    	neigh_queue_free(&priv->neigh_q);
    
    	if (priv->cfg.history) {
    
    		neigh_history_store_to_json_file(priv, priv->cfg.history_file);
    
    		hosts_store_to_json_file(priv, priv->cfg.hosts_file);
    	}
    
    Jakob Olsson's avatar
    Jakob Olsson committed
    	neigh_flowtable_flush(priv);
    	neigh_iptable_flush(priv);
    	hostmngr_free_interfaces(priv);
    
    
    	neigh_history_free(&priv->neigh_q);
    
    	hostlist_flush(priv);
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    	return;
    }
    
    
    int hostmngr_init(void **priv, void *opts)
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    {
    
    	struct hostmngr_useropts *uopts = (struct hostmngr_useropts *)opts;
    	struct hostmngr_private *p;
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    	int ret;
    
    
    
    	set_sighandler(SIGUSR2, hostmngr_sighandler);
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    	set_sighandler(SIGPIPE, SIG_IGN);
    	/* and SIGINT/SIGTERM handlers from uloop cancel uloop */
    
    	*priv = NULL;
    
    	p = calloc(1, sizeof(struct hostmngr_private));
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    	if (!p)
    		return -1;
    
    
    	uloop_init();
    	p->bus = ubus_connect(uopts->ubus_sockpath);
    	if (!p->bus) {
    		err("Failed to connect to ubus\n");
    		goto out_err;
    	}
    	ubus_add_uloop(p->bus);
    
    
    	hostmngr_config_defaults(&p->cfg);
    	ret = hostmngr_reconfig(&p->cfg, uopts->confpath, uopts->conffile);
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    	if (ret) {
    		err("Invalid config\n");
    		goto out_err;
    	}
    
    	hostmngr_dump_config(&p->cfg);
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    
    
    	ret = hostmngr_init_private(p, opts);
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    	if (ret)
    		goto out_err;
    
    
    	ret = hostmngr_publish_object(p, uopts->objname);
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    	if (ret)
    		goto out_err;
    
    
    	ret = hostmngr_register_local_events(p);
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    	if (ret)
    		goto out_err;
    
    
    	ret = hostmngr_register_nlevents(p);
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    	if (ret)
    		goto out_err;
    
    	timer_init(&p->hbtimer, heartbeat_timer_cb);
    
    	timer_init(&p->refreshtimer, hostmngr_periodic_refresh);
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    
    	timer_set(&p->hbtimer, 1000);
    
    	timer_set(&p->refreshtimer, 2000);
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    
    	*priv = p;
    	return 0;
    
    out_err:
    	uloop_done();
    	free(p);
    	return -1;
    }
    
    
    void hostmngr_run(void *handle)
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    {
    	UNUSED(handle);
    
    	uloop_run();
    }
    
    
    int hostmngr_exit(void *handle)
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    {
    
    	struct hostmngr_private *priv = (struct hostmngr_private *)handle;
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    
    	if (!priv)
    		return 0;
    
    	timer_del(&priv->hbtimer);
    	timer_del(&priv->refreshtimer);
    
    
    Jakob Olsson's avatar
    Jakob Olsson committed
    	nfct_cleanup();
    
    
    	hostmngr_unregister_local_events(priv);
    	hostmngr_unregister_nlevents(priv);
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    
    
    	hostmngr_remove_object(priv);
    	hostmngr_exit_private(priv);
    
    Jakob Olsson's avatar
    Jakob Olsson committed
    	hostmngr_config_free(&priv->cfg);
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    
    	ubus_free(priv->bus);
    	uloop_done();
    	free(priv);
    
    	dbg("hostmngr_exit\n");
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    	return 0;
    }
    
    
    int hostmngr_main(void *useropts)
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    {
    
    	struct hostmngr_useropts *opts = (struct hostmngr_useropts *)useropts;
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    	void *ctx;
    	int ret;
    
    
    	if (opts->daemonize)
    		do_daemonize(opts->pidfile);
    
    	start_logging(opts);
    
    
    	ret = hostmngr_init(&ctx, opts);
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    	if (ret) {
    
    		err("%s : Failed to init.\n", HOSTS_OBJECT);