+#ifdef NMRPFLASH_LINUX
+static int bridge_stp_state(const char *intf)
+{
+ char name[256];
+ snprintf(name, sizeof(name), "/sys/class/net/%s/bridge/stp_state", intf);
+ return open(name, O_RDWR, 0644);
+}
+
+static bool bridge_stp_enabled(const char *intf)
+{
+ char c;
+ int fd = bridge_stp_state(intf);
+ if (fd == -1) {
+ return false;
+ }
+
+ if (read(fd, &c, 1) != 1) {
+ c = '0';
+ }
+
+ close(fd);
+ return c == '1';
+}
+
+static bool bridge_stp(const char *intf, bool enabled)
+{
+ bool ret;
+ const char *s = enabled ? "1\n" : "0\n";
+ int fd = bridge_stp_state(intf);
+ if (fd == -1) {
+ return false;
+ }
+
+ ret = (write(fd, s, 2) == 2);
+ close(fd);
+
+ return ret;
+}
+
+static struct nl_addr *build_ip(uint32_t ip)
+{
+ struct nl_addr *na = nl_addr_build(AF_INET, &ip, 4);
+ if (!na) {
+ xperror("nl_addr_build");
+ }
+
+ return na;
+}
+
+static struct nl_sock *xnl_socket_route()
+{
+ int err;
+ struct nl_sock *sk = nl_socket_alloc();
+ if (sk) {
+ if (!(err = nl_connect(sk, NETLINK_ROUTE))) {
+ return sk;
+ }
+ nl_socket_free(sk);
+ nl_perror(err, "nl_connect");
+ } else {
+ xperror("nl_socket_alloc");
+ }
+
+ return NULL;
+}
+
+static bool intf_add_del_ip(const char *intf, uint32_t ipaddr, uint32_t ipmask, bool add)
+{
+ struct rtnl_addr *ra = NULL;
+ struct nl_sock *sk = NULL;
+ struct nl_addr *na = NULL;
+ int err = 1;
+
+ if (!(sk = xnl_socket_route())) {
+ return false;
+ }
+
+ if (!(ra = rtnl_addr_alloc())) {
+ xperror("rtnl_addr_alloc");
+ goto out;
+ }
+
+ rtnl_addr_set_ifindex(ra, if_nametoindex(intf));
+
+ if (!(na = build_ip(ipaddr))) {
+ goto out;
+ }
+
+ nl_addr_set_prefixlen(na, bitcount(ipmask));
+ rtnl_addr_set_local(ra, na);
+ nl_addr_put(na);
+
+ if (!(na = build_ip((ipaddr & ipmask) | ~ipmask))) {
+ goto out;
+ }
+
+ rtnl_addr_set_broadcast(ra, na);
+ nl_addr_put(na);
+
+ if ((err = add ? rtnl_addr_add(sk, ra, 0) : rtnl_addr_delete(sk, ra, 0)) < 0) {
+ if (add && err == -NLE_EXIST) {
+ err = 0;
+ } else if (add || verbosity > 1) {
+ nl_perror(err, add ? "rtnl_addr_add" : "rtnl_addr_delete");
+ }
+ }
+
+out:
+ rtnl_addr_put(ra);
+ nl_socket_free(sk);
+
+ return !err;
+}
+
+static bool intf_add_del_arp(const char *intf, uint32_t ipaddr, uint8_t *hwaddr, bool add)
+{
+#if 0
+ struct arpreq arp;
+ memset(&arp, 0, sizeof(arp));
+ arp.arp_ha.sa_family = ARPHRD_ETHER;
+ memcpy(&arp.arp_ha.sa_data, hwaddr, 6);
+ arp.arp_flags = ATF_PERM | ATF_COM;
+
+ struct sockaddr_in *in = (struct sockaddr_in*)&req.arp_pa;
+ in->sin_addr.s_addr = htonl(ipaddr);
+ in->sin_family = AF_INET;
+
+ int fd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (fd < 0) {
+ perror("socket");
+ return false;
+ }
+
+ bool ret = true;
+
+ if (ioctl(fd, add ? SIOCSARP : SIOCDARP, &req) < 0) {
+ perror(add ? "ioctl(SIOCSARP)" : "ioctl(SIOCDARP");
+ ret = false;
+ }
+
+ close(fd);
+ return ret;
+#else
+ struct nl_sock *sk;
+ struct rtnl_neigh *neigh;
+ struct nl_addr *mac, *ip;
+ int err = 1;
+
+ sk = NULL;
+ neigh = NULL;
+ mac = ip = NULL;
+
+ if (!(sk = xnl_socket_route())) {
+ goto out;
+ }
+
+ if (!(neigh = rtnl_neigh_alloc())) {
+ xperror("rtnl_neigh_alloc");
+ goto out;
+ }
+
+ if (!(mac = nl_addr_build(AF_PACKET, hwaddr, 6))) {
+ xperror("nl_addr_build");
+ goto out;
+ }
+
+ if (!(ip = nl_addr_build(AF_INET, &ipaddr, 4))) {
+ xperror("nl_addr_build");
+ goto out;
+ }
+
+ rtnl_neigh_set_ifindex(neigh, if_nametoindex(intf));
+ rtnl_neigh_set_dst(neigh, ip);
+
+ err = rtnl_neigh_delete(sk, neigh, 0);
+
+ if (add) {
+ rtnl_neigh_set_lladdr(neigh, mac);
+ rtnl_neigh_set_state(neigh, NUD_PERMANENT);
+ err = rtnl_neigh_add(sk, neigh, NLM_F_CREATE);
+ }
+
+ if (err && (add || verbosity > 1)) {
+ nl_perror(err, add ? "rtnl_neigh_add" : "rtnl_neigh_delete");
+ }
+
+out:
+ nl_addr_put(ip);
+ nl_addr_put(mac);
+ rtnl_neigh_put(neigh);
+ nl_socket_free(sk);
+
+ return !err;
+#endif
+}
+
+#endif
+
+static bool intf_get_info(const char *intf, uint8_t *hwaddr, bool *bridge)