tech-net archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

tap(4) interface management



Hi,
   regarding btpand(8) which uses a tap(4) device to provide Ethernet over
Bluetooth services, I have been looking at adding some features relating
to the daemon actively monitoring the interface for various settings
rather than having them fixed at startup or requiring a config file. Such
as:

1. btpand currently exits in client mode if the connection is lost, but I
   also wanted it to exit if the interface is marked down locally.

2. btpand running as a server registers a service record with the service
   discovery daemon, including protocols available and subnet masks for
   IPv4 and IPv6. I want to provide the actual values in use.

3. the BNEP protocol has filtering options (mandatory to support) to cut
   out unwanted ethernet packet types on a limited bandwidth connection.
   btpand could monitor the address families configured on the tap interface
   and enable/disable protocols as required.

  Some of this can be handled as-is with a routing socket (as per attached
patch) but not quite all as it only notifies changes to the IFF_UP flag
and interface addresses and I would additionally like to notice changes to
other flags (IFF_NOARP at least, possibly IFF_LINKn etc later) and the
interface media settings (which can map to elements in the service
record).

  So, I'm thinking of how to get where I'm wanting to be, the options seem
to be either extending the routing socket to provide additional messages,
or extending the tap driver to be able to signal changes to its owner.

  Extending the routing socket is possible although its a global
interface. A RTM_IFINFO message is currently sent out when interface
status is changed, but it does contain the entire if_flags field and is
not only for IFF_UP changes (so, extra messages are not problematic). Its
fairly simple to emit messages for SIOCSIFFLAGS (as per second attached
patch) but I haven't found any way to get the media settings this way, it
would require adding a RTM_MEDIA message type which I haven't looked into.

  Extending the tap driver would be less invasive but unfortunately the
tap IO is carried out on a file descriptor so is no way (?) to do out of
band communication (socket control messages would have been great for
this), the best I can come up with is to have an ioctl enable (eg) SIGUSR
to be raised on informational changes and have the daemon check.

thoughts and suggestions?

iain
? TODO
Index: Makefile
===================================================================
RCS file: /cvsroot/src/usr.sbin/btpand/Makefile,v
retrieving revision 1.5
diff -u -r1.5 Makefile
--- Makefile    12 May 2009 21:50:38 -0000      1.5
+++ Makefile    13 May 2009 13:58:44 -0000
@@ -3,7 +3,7 @@
 
 PROG=  btpand
 MAN=   btpand.8
-SRCS=  btpand.c bnep.c channel.c client.c packet.c server.c tap.c
+SRCS=  btpand.c bnep.c channel.c client.c packet.c rtsock.c server.c tap.c
 
 DPADD+=        ${LIBBLUETOOTH} ${LIBEVENT} ${LIBUTIL}
 LDADD+=        -lbluetooth -levent -lutil
Index: btpand.c
===================================================================
RCS file: /cvsroot/src/usr.sbin/btpand/btpand.c,v
retrieving revision 1.4
diff -u -r1.4 btpand.c
--- btpand.c    12 May 2009 21:50:38 -0000      1.4
+++ btpand.c    13 May 2009 13:58:44 -0000
@@ -222,6 +222,7 @@
                server_init();
                client_init();
                tap_init();
+               rtsock_init();
 
                main_detach();
 
Index: btpand.h
===================================================================
RCS file: /cvsroot/src/usr.sbin/btpand/btpand.h,v
retrieving revision 1.3
diff -u -r1.3 btpand.h
--- btpand.h    12 May 2009 21:50:38 -0000      1.3
+++ btpand.h    13 May 2009 13:58:44 -0000
@@ -189,8 +189,13 @@
 pkthdr_t *     pkthdr_alloc(packet_t *);
 void           pkthdr_free(pkthdr_t *);
 
+/* rtsock.c */
+void           rtsock_init(void);
+
 /* server.c */
 void           server_init(void);
+void           server_addproto(uint16_t, ...);
+void           server_delproto(uint16_t);
 
 /* tap.c */
 void           tap_init(void);
Index: rtsock.c
===================================================================
RCS file: rtsock.c
diff -N rtsock.c
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ rtsock.c    13 May 2009 13:58:44 -0000
@@ -0,0 +1,321 @@
+/*     $NetBSD: rtsock.c$      */
+
+/*-
+ * Copyright (c) 2009 Iain Hibbert
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: rtsock.c$");
+
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+
+#include <net/if.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+
+#include <unistd.h>
+
+#include "btpand.h"
+
+static unsigned int    tap_index;
+static struct in_addr  tap_ipv4_addr;
+static struct in_addr  tap_ipv4_mask;
+static struct in6_addr tap_ipv6_addr;
+static struct in6_addr tap_ipv6_mask;
+
+static struct event    rtsock_ev;
+static uint8_t         rtsock_buf[2048];
+
+static void rtsock_read(int, short, void *);
+static void rtsock_msghdr(uint8_t *, size_t);
+static void rtsock_newaddr(uint8_t *);
+static void rtsock_deladdr(uint8_t *);
+static void rtsock_ifinfo(uint8_t *);
+
+/* rtmsg sockaddr alignment from src/sys/net/rtsock.c */
+#define ROUNDUP(a) \
+       ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
+
+#define SIN4_ADDR(sa)  (((struct sockaddr_in *)(sa))->sin_addr)
+#define SIN6_ADDR(sa)  (((struct sockaddr_in6 *)(sa))->sin6_addr)
+
+/*
+ * Set up a routing socket to monitor changes to the network
+ * interface, and load the initial state
+ */
+void
+rtsock_init(void)
+{
+       uint8_t *buf;
+       size_t len;
+       int mib[6], s;
+
+       tap_index = if_nametoindex(interface_name);
+       if (tap_index == 0) {
+               log_err("Could not get %s index: %m", interface_name);
+               exit(EXIT_FAILURE);
+       }
+
+       s = socket(PF_ROUTE, SOCK_RAW, AF_UNSPEC);
+       if (s == -1) {
+               log_err("Could not open PF_ROUTE socket: %m");
+               exit(EXIT_FAILURE);
+       }
+
+       event_set(&rtsock_ev, s, EV_READ | EV_PERSIST, rtsock_read, NULL);
+       if (event_add(&rtsock_ev, NULL) == -1) {
+               log_err("Could not add rtsock event: %m");
+               exit(EXIT_FAILURE);
+       }
+
+       mib[0] = CTL_NET;
+       mib[1] = PF_ROUTE;
+       mib[2] = 0;             /* protocol */
+       mib[3] = 0;             /* address family */
+       mib[4] = NET_RT_IFLIST;
+       mib[5] = 0;             /* no flags */
+
+       if (sysctl(mib, __arraycount(mib), NULL, &len, NULL, 0) == -1
+           || (buf = malloc(len)) == NULL
+           || sysctl(mib, __arraycount(mib), buf, &len, NULL, 0) == -1) {
+               log_err("failed to get route information");
+               exit(EXIT_FAILURE);
+       }
+
+       rtsock_msghdr(buf, len);
+       free(buf);
+}
+
+static void
+rtsock_read(int fd, short ev, void *arg)
+{
+       ssize_t nr;
+
+       nr = read(fd, rtsock_buf, sizeof(rtsock_buf));
+       if (nr == -1) {
+               log_err("rtsock read error: %m");
+               exit(EXIT_FAILURE);
+       }
+
+       rtsock_msghdr(rtsock_buf, (size_t)nr);
+}
+
+static void
+rtsock_msghdr(uint8_t *next, size_t nr)
+{
+       struct rt_msghdr *rtm;
+
+       while (nr > 0) {
+               rtm = (struct rt_msghdr *)next;
+
+               if (offsetof(struct rt_msghdr, rtm_msglen)
+                   + sizeof(rtm->rtm_msglen) > nr || rtm->rtm_msglen > nr) {
+                       log_err("received truncated rtsock message");
+                       break;
+               }
+
+               switch (rtm->rtm_type) {
+               case RTM_NEWADDR:
+                       rtsock_newaddr(next);
+                       break;
+
+               case RTM_DELADDR:
+                       rtsock_deladdr(next);
+                       break;
+
+               case RTM_IFINFO:
+                       rtsock_ifinfo(next);
+                       break;
+
+               default:
+                       break;
+               }
+
+               nr -= rtm->rtm_msglen;
+               next += rtm->rtm_msglen;
+       }
+}
+
+static void
+rtsock_newaddr(uint8_t *next)
+{
+       struct ifa_msghdr *ifam;
+       struct sockaddr *sa;
+       int i;
+
+       ifam = (struct ifa_msghdr *)next;
+       next += sizeof(struct ifa_msghdr);      /* ROUNDUP? */
+
+       if (ifam->ifam_index != tap_index)
+               return;
+
+       for (i = 0; i < RTAX_MAX; i++) {
+               if ((ifam->ifam_addrs & (1 << i)) == 0)
+                       continue;
+
+               sa = (struct sockaddr *)next;
+               next += ROUNDUP(sa->sa_len);
+
+               switch (i) {
+               case RTAX_NETMASK:
+                       log_debug("new NETMASK family %d", sa->sa_family);
+
+                       switch (sa->sa_family) {
+                       case AF_INET:
+                               tap_ipv4_mask = SIN4_ADDR(sa);
+                               break;
+
+                       case AF_INET6:
+                               tap_ipv6_mask = SIN6_ADDR(sa);
+                               break;
+
+                       default:
+                               break;
+                       }
+
+                       break;
+
+               case RTAX_IFA:
+                       log_debug("new IFA family %d", sa->sa_family);
+
+                       switch (sa->sa_family) {
+                       case AF_INET:
+                               tap_ipv4_addr = SIN4_ADDR(sa);
+                               server_addproto(ETHERTYPE_IP,
+                                   &tap_ipv4_addr, &tap_ipv4_mask);
+
+                               break;
+
+                       case AF_INET6:
+                               /* XXX I think we need to ignore IPv6 loopback 
address -- how? */
+                               tap_ipv6_addr = SIN6_ADDR(sa);
+                               server_addproto(ETHERTYPE_IPV6,
+                                   &tap_ipv6_addr, &tap_ipv6_mask);
+
+                               break;
+
+                       default:
+                               break;
+                       }
+
+                       break;
+
+               default:
+                       break;
+               }
+       }
+}
+
+static void
+rtsock_deladdr(uint8_t *next)
+{
+       struct ifa_msghdr *ifam;
+       struct sockaddr *sa;
+       int i;
+
+       ifam = (struct ifa_msghdr *)next;
+       next += sizeof(struct ifa_msghdr);      /* ROUNDUP? */
+
+       if (ifam->ifam_index != tap_index)
+               return;
+
+       for (i = 0; i < RTAX_MAX; i++) {
+               if ((ifam->ifam_addrs & (1 << i)) == 0)
+                       continue;
+
+               sa = (struct sockaddr *)next;
+               next += ROUNDUP(sa->sa_len);
+
+               if (i != RTAX_IFA)
+                       continue;
+
+               switch (i) {
+               case RTAX_NETMASK:
+                       log_debug("del NETMASK family %d", sa->sa_family);
+
+                       switch (sa->sa_family) {
+                       case AF_INET:
+                               memset(&tap_ipv4_mask, 0xff, 
sizeof(tap_ipv4_mask));
+                               break;
+
+                       case AF_INET6:
+                               memset(&tap_ipv6_mask, 0xff, 
sizeof(tap_ipv6_mask));
+                               break;
+
+                       default:
+                               break;
+                       }
+
+                       break;
+
+               case RTAX_IFA:
+                       log_debug("del IFA family %d", sa->sa_family);
+
+                       switch (sa->sa_family) {
+                       case AF_INET:
+                               server_delproto(ETHERTYPE_IP);
+                               break;
+
+                       case AF_INET6:
+                               server_delproto(ETHERTYPE_IPV6);
+                               break;
+
+                       default:
+                               break;
+                       }
+
+                       break;
+
+               default:
+                       break;
+               }
+       }
+}
+
+static void
+rtsock_ifinfo(uint8_t *next)
+{
+       struct if_msghdr *ifm;
+
+       ifm = (struct if_msghdr *)next;
+       next += sizeof(struct if_msghdr);       /* ROUNDUP? */
+
+       if (ifm->ifm_index != tap_index)
+               return;
+
+       if ((ifm->ifm_flags & IFF_UP) == 0) {
+               log_info("%s: marked down, exiting", interface_name);
+               exit(EXIT_SUCCESS);
+       }
+
+       log_debug("new %s flags 0x%hx", interface_name, ifm->ifm_flags);
+
+       /* XXX a RTM_IFINFO message is not generated if IFF_NOARP changes -- 
fix */
+       if ((ifm->ifm_flags & IFF_NOARP) == 0)
+               server_addproto(ETHERTYPE_ARP);
+       else
+               server_delproto(ETHERTYPE_ARP);
+}
Index: server.c
===================================================================
RCS file: /cvsroot/src/usr.sbin/btpand/server.c,v
retrieving revision 1.4
diff -u -r1.4 server.c
--- server.c    12 May 2009 21:50:38 -0000      1.4
+++ server.c    13 May 2009 13:58:44 -0000
@@ -30,6 +30,8 @@
 
 #include <sys/ioctl.h>
 
+#include <arpa/inet.h>
+
 #include <net/ethertypes.h>
 
 #include <bluetooth.h>
@@ -49,14 +51,15 @@
 
 static char *          server_ipv4_subnet;
 static char *          server_ipv6_subnet;
-static uint16_t                server_proto[] = { ETHERTYPE_IP, ETHERTYPE_ARP, 
ETHERTYPE_IPV6 };
-static size_t          server_nproto = __arraycount(server_proto);
+static uint16_t                server_proto[4];        /* IPv4, ARP & IPv6 are 
known */
+static size_t          server_nproto;
 
 static void server_open(void);
 static void server_read(int, short, void *);
 static void server_down(channel_t *);
 static void server_update(void);
 static void server_mkrecord(void);
+static void server_mksubnet(int, size_t, char **, uint8_t *, uint8_t *);
 
 void
 server_init(void)
@@ -367,3 +370,130 @@
        server_record.next = data;
        server_record.end = buf.next;
 }
+
+void
+server_addproto(uint16_t proto, ...)
+{
+       uint8_t *addr, *mask;
+       va_list ap;
+       size_t i;
+       uint16_t tmp;
+
+       log_debug("add Ethertype 0x%04x", proto);
+
+       if (server_limit == 0)
+               return;
+
+       va_start(ap, proto);
+
+       switch (proto) {
+       case ETHERTYPE_IP: {
+               addr = va_arg(ap, uint8_t *);
+               mask = va_arg(ap, uint8_t *);
+               server_mksubnet(AF_INET, sizeof(struct in_addr),
+                   &server_ipv4_subnet, addr, mask);
+
+               break;
+               }
+
+       case ETHERTYPE_IPV6: {
+               addr = va_arg(ap, uint8_t *);
+               mask = va_arg(ap, uint8_t *);
+               server_mksubnet(AF_INET6, sizeof(struct in6_addr),
+                   &server_ipv6_subnet, addr, mask);
+
+               break;
+               }
+
+       default:
+               break;
+       }
+
+       va_end(ap);
+
+       /* insert the protocol to our list */
+       for (i = 0; ; i++) {
+               if (i == server_nproto) {
+                       assert(server_nproto < __arraycount(server_proto));
+                       server_proto[server_nproto++] = proto;
+                       break;
+               }
+
+               if (proto == server_proto[i])
+                       break;
+
+               if (proto < server_proto[i]) {
+                       tmp = server_proto[i];
+                       server_proto[i] = proto;
+                       proto = tmp;
+               }
+       }
+
+       server_update();
+}
+
+void
+server_delproto(uint16_t proto)
+{
+       size_t i;
+
+       log_debug("del Ethertype 0x%04x", proto);
+
+       if (server_limit == 0)
+               return;
+
+       switch (proto) {
+       case ETHERTYPE_IP:
+               free(server_ipv4_subnet);
+               server_ipv4_subnet = NULL;
+               break;
+
+       case ETHERTYPE_IPV6:
+               free(server_ipv6_subnet);
+               server_ipv6_subnet = NULL;
+               break;
+
+       default:
+               break;
+       }
+
+       /* remove the protocol from the list */
+       for (i = 0; i < server_nproto; i++) {
+               if (proto == server_proto[i]) {
+                       server_nproto--;
+                       for (; i < server_nproto; i++)
+                               server_proto[i] = server_proto[i + 1];
+               }
+       }
+
+       server_update();
+}
+
+static void
+server_mksubnet(int family, size_t len, char **str, uint8_t *addr, uint8_t 
*mask)
+{
+       char buf[INET6_ADDRSTRLEN];
+       uint8_t src[sizeof(struct in6_addr)];
+       size_t bits, i, j;
+
+       assert(len <= sizeof(src));
+
+       bits = 0;
+
+       for (i = 0; i < len; i++) {
+               for (j = 0; j < 8; j++) {
+                       if (mask[i] & (1 << j))
+                               bits++;
+               }
+
+               src[i] = (addr[i] & mask[i]);
+       }
+
+       inet_ntop(family, src, buf, sizeof(buf));
+
+       free(*str);
+       if (asprintf(str, "%s/%d", buf, bits) == -1)
+               *str = NULL;
+       else
+               log_debug("family %d subnet: \"%s\"", family, *str);
+}
Common subdirectories: /usr/src/sys/net/agr/CVS and agr/CVS
--- /usr/src/sys/net/if.c       2009-02-24 10:05:47.000000000 +0000
+++ if.c        2009-04-06 17:12:15.000000000 +0100
@@ -1481,8 +1481,12 @@ ifioctl_common(struct ifnet *ifp, u_long
                        if_up(ifp);
                        splx(s);
                }
-               ifp->if_flags = (ifp->if_flags & IFF_CANTCHANGE) |
-                       (ifr->ifr_flags &~ IFF_CANTCHANGE);
+               if (((ifr->ifr_flags ^ ifp->if_flags) & ~IFF_CANTCHANGE)) {
+                       ifp->if_flags = (ifp->if_flags & IFF_CANTCHANGE)
+                           | (ifr->ifr_flags & ~IFF_CANTCHANGE);
+
+                       rt_ifmsg(ifp);
+               }
                break;
        case SIOCGIFFLAGS:
                ifr = data;


Home | Main Index | Thread Index | Old Index