From 1c0caf15d6ccf807ebd74360c34e0404483e4f76 Mon Sep 17 00:00:00 2001 From: Anjan Chanda <anjan.chanda@iopsys.eu> Date: Thu, 15 Jun 2023 10:58:27 +0200 Subject: [PATCH] decide on neigh's reachability from received arp response --- src/hostmngr.c | 131 ++++++++++++++++++++++++++++++++++++++++++++++++- src/neigh.c | 65 +++++++++++++++++++++++- src/neigh.h | 3 ++ src/netlink.c | 21 +++++--- src/topology.h | 18 ++++++- src/ubus.c | 16 +++--- 6 files changed, 234 insertions(+), 20 deletions(-) diff --git a/src/hostmngr.c b/src/hostmngr.c index fed6a85..0fdf5ea 100644 --- a/src/hostmngr.c +++ b/src/hostmngr.c @@ -12,12 +12,16 @@ #include <stdlib.h> #include <stdint.h> #include <errno.h> +#include <unistd.h> +#include <fcntl.h> #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> #include <netlink/route/link.h> @@ -76,6 +80,7 @@ void enum_interfaces_cb(struct nl_object *obj, void *arg) strncpy(iface->ifname, rtnl_link_get_name(link), 16); iface->ifindex = rtnl_link_get_ifindex(link); iface->exclude = 1; + iface->priv = NULL; a = rtnl_link_get_addr(link); if (nl_addr_get_len(a) == 6) @@ -112,6 +117,8 @@ void enum_interfaces_cb(struct nl_object *obj, void *arg) iface->mediatype = IF_MEDIA_ETH; } + topology_register_arp_sock(iface->ifname, &iface->arpsk, iface); + list_add_tail(&iface->list, argp->iflist); *argp->n += 1; @@ -158,6 +165,18 @@ int topology_enumerate_interfaces(struct topologyd_private *priv) return enum_interfaces(&priv->iflist, &priv->num_local_interfaces); } +static void topology_init_interface_private(struct topologyd_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; + } +} + void topology_print_interfaces(struct topologyd_private *priv) { struct local_interface *e; @@ -452,6 +471,102 @@ int topology_register_notifier_file(struct topologyd_private *priv, const char * 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) { + //int ret = -1; + //socklen_t ret_len = sizeof(ret); + + dbg("%s: - error -\n", __func__); + 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 topologyd_private *priv = (struct topologyd_private *)iface->priv; + + dbg("ARP received from " MACFMT"\n", MAC2STR(eh->h_source)); + neigh_mark_reachable(&priv->neigh_q, eh->h_source, iface->ifname); + } + } +} + +int topology_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; + + return 0; +} + +void topology_unregister_arp_sock(struct arp_sock *arpsk) +{ + if (arpsk) { + uloop_fd_delete(&arpsk->uloop); + close(arpsk->uloop.fd); + arpsk->priv = NULL; + } +} + char *topology_brport_to_ifname(struct topologyd_private *priv, uint16_t brport) { struct local_interface *iface; @@ -488,12 +603,22 @@ struct local_interface *topologyd_ifname_to_interface(struct topologyd_private * } int topology_handle_ethport_carrier_off(struct topologyd_private *priv, - const char *ifname, int off) + const char *ifname) +{ + if (!priv || !ifname) + return -1; + + neigh_set_unreachable(&priv->neigh_q, ifname, 1); + return 0; +} + +int topology_handle_ethport_carrier_on(struct topologyd_private *priv, + const char *ifname) { if (!priv || !ifname) return -1; - neigh_set_unreachable(&priv->neigh_q, ifname, off); + neigh_probe_unreachable(&priv->neigh_q, ifname); return 0; } @@ -554,6 +679,7 @@ int topologyd_init_private(struct topologyd_private *priv, void *useropts) if (!ret) topology_print_interfaces(priv); + topology_init_interface_private(priv); topology_include_interface(priv, DEFAULT_LAN_IFNAME); topology_exclude_interfaces(priv, useropts); @@ -565,6 +691,7 @@ int topologyd_init_private(struct topologyd_private *priv, void *useropts) topology_get_interface_neighbors(priv); topology_get_neighbors_hostname(priv); + //topology_register_arp_sock("br-lan", &priv->arpsk, priv); topology_register_notifier_file(priv, "/var/dhcp.leases"); nfct_get_entries_nolo(priv); diff --git a/src/neigh.c b/src/neigh.c index abe661e..db09db5 100644 --- a/src/neigh.c +++ b/src/neigh.c @@ -649,7 +649,7 @@ void neigh_set_unreachable(void *nq, const char *ifname, int val) //struct neigh_history_entry *he = NULL; e->unreachable = val; - dbg(MACFMT " marking it %sreachable\n", + err(MACFMT " marking it %sreachable\n", MAC2STR(e->macaddr), val ? "un" : ""); /* @@ -665,6 +665,69 @@ void neigh_set_unreachable(void *nq, const char *ifname, int val) } } +void neigh_mark_reachable(void *nq, uint8_t *macaddr, const char *ifname) +{ + struct neigh_queue *q = (struct neigh_queue *)nq; + struct neigh_entry *e = NULL; + int idx = 0; + + if (!macaddr || hwaddr_is_zero(macaddr) || !ifname) + return; + + for (idx = 0; idx < NEIGH_ENTRIES_MAX; idx++) { + if (hlist_empty(&q->table[idx])) + continue; + + hlist_for_each_entry(e, &q->table[idx], hlist) { + if (hwaddr_equal(e->macaddr, macaddr)) { + //struct neigh_history_entry *he = NULL; + + e->unreachable = 0; + err("Marking " MACFMT " reachable through %s\n", + MAC2STR(e->macaddr), ifname); + + memset(e->ifname, 0, sizeof(e->ifname)); + strncpy(e->ifname, ifname, strlen(ifname)); + /* + he = neigh_history_lookup(nq, e->macaddr); + if (he) { + dbg("Update history last-seen time\n"); + time(&he->lastseen); + he->alive = false; + } + */ + } + } + } +} + +void neigh_probe_unreachable(void *nq, const char *ifname) +{ + struct neigh_queue *q = (struct neigh_queue *)nq; + struct neigh_entry *e = NULL; + int idx = 0; + + for (idx = 0; idx < NEIGH_ENTRIES_MAX; idx++) { + if (hlist_empty(&q->table[idx])) + continue; + + hlist_for_each_entry(e, &q->table[idx], hlist) { + if (e->unreachable) { + char cmd[256] = {0}; + char ipbuf[46] = {0}; + + if (e->ipv4.addr.ip4.s_addr == INADDR_ANY) + continue; + + inet_ntop(e->ipv4.family, &e->ipv4.addr, ipbuf, sizeof(ipbuf)); + dbg("arping %s ...\n", ipbuf); + snprintf(cmd, 255, "arping -I %s -c 1 -w 1 -f %s &", "br-lan" /* ifname */, ipbuf); //FIXME + runCmd(cmd); /* Flawfinder: ignore */ + } + } + } +} + int neigh_set_type(void *nq, uint8_t *macaddr, enum neigh_type type) { struct neigh_entry *e = NULL; diff --git a/src/neigh.h b/src/neigh.h index e5f6803..e3d34c4 100644 --- a/src/neigh.h +++ b/src/neigh.h @@ -151,6 +151,9 @@ extern void neigh_queue_flush(void *q); struct neigh_entry *neigh_queue_print(void *q); void neigh_set_unreachable(void *nq, const char *ifname, int val); +void neigh_probe_unreachable(void *nq, const char *ifname); +void neigh_mark_reachable(void *nq, uint8_t *macaddr, const char *ifname); + int neigh_set_type(void *q, uint8_t *macaddr, enum neigh_type type); uint16_t neigh_get_brport(void *q, uint8_t *macaddr); int neigh_set_1905(void *q, uint8_t *macaddr); diff --git a/src/netlink.c b/src/netlink.c index 67551c1..9c2947c 100644 --- a/src/netlink.c +++ b/src/netlink.c @@ -215,8 +215,10 @@ static int topologyd_handle_neigh_tbl_change(struct topologyd_private *priv, boo } /* update bridge port_nos on which the hosts are last seen */ - if (if_isbridge(ifname)) + if (if_isbridge(ifname)) { + dbg("%s: update brport number\n", __func__); topologyd_update_neigh_brport(priv, ifname); + } #if 1 //def NEIGH_DEBUG if (priv->neigh_q.pending_cnt > 0) { @@ -266,8 +268,8 @@ static int topologyd_handle_nlevents_neigh(struct topologyd_private *priv, if (!iface || iface->exclude) return NL_SKIP; - dbg("%s: Neigh " MACFMT " ip = %s on %s, state = %s\n", - __func__, MAC2STR(macaddr), ipbuf, ifname, + dbg("%s: [ %s ] Neigh " MACFMT " ip = %s on %s, state = %s\n", + __func__, add ? "NEW" : "DEL", MAC2STR(macaddr), ipbuf, ifname, rtnl_neigh_state2str(ndm->ndm_state, state, sizeof(state))); @@ -307,10 +309,13 @@ static int topologyd_handle_nlevents_link(struct topologyd_private *priv, if (iface) iface->ifflags = ifi->ifi_flags; - if (if_isbridge(ifname)) { - dbg("%s: %s: %s (ifindex = %d) bridge\n", __func__, - add ? "NEWLINK" : "DELLINK", ifname, ifi->ifi_index); + char flagstr[256] = {0}; + rtnl_link_flags2str(ifi->ifi_flags, flagstr, sizeof(flagstr)); + err("%s: %s: %s (ifindex = %d) family = %d, flags = 0x%x %s --------->\n", __func__, + add ? "NEWLINK" : "DELLINK", ifname, ifi->ifi_index, ifi->ifi_family, ifi->ifi_flags, flagstr); + + if (if_isbridge(ifname)) { #if 0 //TODO if (topologyd_lookup_interface_in_config(priv, ifname)) { struct topologyd_interface *ifs; @@ -347,11 +352,11 @@ static int topologyd_handle_nlevents_link(struct topologyd_private *priv, return NL_SKIP; } - dbg("%s: %s : %s (" MACFMT ", %d), master = %d, fam = %d, flags = 0x%x\n", + dbg("%s: %s : %s (" MACFMT ", %d), master = %d, fam = %d, flags = 0x%x %s\n", __func__, add ? "NEWLINK" : "DELLINK", ifname, MAC2STR(macaddr), ifi->ifi_index, br_ifindex, ifi->ifi_family, - ifi->ifi_flags); + ifi->ifi_flags, flagstr); #if 0 //TODO diff --git a/src/topology.h b/src/topology.h index cd51c81..ca453e7 100644 --- a/src/topology.h +++ b/src/topology.h @@ -10,6 +10,16 @@ #ifndef TOPOLOGY_H #define TOPOLOGY_H + +struct arp_sock { + struct uloop_fd uloop; + void *priv; +}; + +int topology_register_arp_sock(const char *ifname, struct arp_sock *arpsk, void *priv); +void topology_unregister_arp_sock(struct arp_sock *arpsk); + + struct i1905_metric { bool br_present; uint32_t tx_errors; @@ -193,6 +203,8 @@ struct local_interface { uint8_t sizeof_mediainfo; uint8_t *mediainfo; /**< media specific data */ + void *priv; + struct arp_sock arpsk; struct list_head list; }; @@ -264,6 +276,8 @@ struct topologyd_private { struct ubus_context *bus; struct ubus_object obj; struct ubus_event_handler evh; + + struct arp_sock arpsk; }; @@ -299,8 +313,8 @@ static inline struct node *topology_get_selfdevice(struct topology_datamodel *dm char *topology_brport_to_ifname(struct topologyd_private *priv, uint16_t brport); struct local_interface *topologyd_ifname_to_interface(struct topologyd_private *priv, const char *ifname); -int topology_handle_ethport_carrier_off(struct topologyd_private *priv, - const char *ifname, int off); +int topology_handle_ethport_carrier_off(struct topologyd_private *priv, const char *ifname); +int topology_handle_ethport_carrier_on(struct topologyd_private *priv, const char *ifname); struct node *alloc_node(uint8_t *macaddr); void free_node(struct node *n); diff --git a/src/ubus.c b/src/ubus.c index 4d21ce8..b6036a5 100644 --- a/src/ubus.c +++ b/src/ubus.c @@ -366,12 +366,13 @@ int topologyd_ubus_show_hosts(struct ubus_context *ctx, struct ubus_object *obj, uint16_t brport; void *wt; - if (e->unreachable) - continue; + //if (e->unreachable) + // continue; neigh_update_ip_entry_stats(p, &e->ipv4, e); aa = blobmsg_open_table(&bb, ""); + blobmsg_add_u8(&bb, "reachable", e->unreachable ? false : true); hwaddr_ntoa(e->macaddr, macstr); blobmsg_add_string(&bb, "macaddress", macstr); blobmsg_add_string(&bb, "hostname", e->hostname); @@ -797,7 +798,7 @@ static void topologyd_ethport_event_handler(struct topologyd_private *p, }; char ifname[16] = {0}, link[8] = {0}; struct blob_attr *tb[4]; - bool up, down; + int down; blobmsg_parse(ev_attr, 4, tb, blob_data(msg), blob_len(msg)); @@ -806,12 +807,13 @@ static void topologyd_ethport_event_handler(struct topologyd_private *p, strncpy(ifname, blobmsg_data(tb[0]), sizeof(ifname) - 1); strncpy(link, blobmsg_data(tb[1]), sizeof(link) - 1); - - up = !strcmp(link, "up"); down = !strcmp(link, "down"); - topology_handle_ethport_carrier_off(p, ifname, down); - UNUSED(up); + dbg("UBUS: %s %s\n", ifname, link); + if (down) + topology_handle_ethport_carrier_off(p, ifname); + else + topology_handle_ethport_carrier_on(p, ifname); return; } -- GitLab