From 03be37db7c2b68293b7ff03a5f54ee8a8f884e8e Mon Sep 17 00:00:00 2001 From: Olaf Seibert Date: Thu, 21 Jan 2016 23:50:27 +0100 Subject: [PATCH] Add initial VDE (libvde2) support. I have compile-tested this on a virtual FreeBSD 10.2 but not run it. --- configure.ac | 4 +- src/Makefile.mk | 1 + src/dpni20.c | 2 +- src/osdnet.c | 338 +++++++++++++++++++++++++++++++----------------- src/osdnet.h | 5 +- 5 files changed, 225 insertions(+), 125 deletions(-) diff --git a/configure.ac b/configure.ac index 577e36c..01f51f2 100644 --- a/configure.ac +++ b/configure.ac @@ -43,6 +43,7 @@ AC_CHECK_LIB([pcap], [pcap_findalldevs]) AC_CHECK_LIB([pcap], [pcap_set_immediate_mode], [AC_DEFINE(HAVE_PCAP_SET_IMMEDIATE_MODE, 1, [Set to 1 if we have pcap_set_immediate_mode])] ) +AC_CHECK_LIB([vdeplug], [vde_send]) NETLIBS="$LIBS" LIBS="$SAVE_LIBS" @@ -51,7 +52,8 @@ AC_CHECK_HEADERS([arpa/inet.h errno.h fcntl.h limits.h netinet/in.h sgtty.h \ stddef.h stdlib.h string.h sys/file.h sys/ioctl.h \ sys/socket.h sys/time.h termios.h unistd.h net/if_tun.h \ linux/if_tun.h linux/if_packet.h net/if_tap.h sys/mtio.h \ - net/nit.h sys/dlpi.h net/if_dl.h net/if_types.h]) + net/nit.h sys/dlpi.h net/if_dl.h net/if_types.h \ + libvdeplug.h]) # Checks for typedefs, structures, and compiler characteristics. AC_C_INLINE diff --git a/src/Makefile.mk b/src/Makefile.mk index f25332a..9bbc699 100644 --- a/src/Makefile.mk +++ b/src/Makefile.mk @@ -103,6 +103,7 @@ showdefs: @echo "MAKER = $(MAKER)" @echo "CFLAGS = $(CFLAGS)" @echo "CFLAGS_AUX = $(CFLAGS_AUX)" + @echo "LDFLAGS = $(LDFLAGS)" @echo "LIBS = $(LIBS)" @echo "NETLIBS = $(NETLIBS)" @echo "CENVFLAGS = $(CENVFLAGS)" diff --git a/src/dpni20.c b/src/dpni20.c index 7377f4a..92233f6 100644 --- a/src/dpni20.c +++ b/src/dpni20.c @@ -910,7 +910,7 @@ void eth_adrset(struct dpni20_s *dpni) /* Apparently won! Try reading it back just to be paranoid, * using packetfilter FD. */ - if (!osn_pfeaget(pfdata.pf_fd, dpni->dpni_ifnam, rdea)) { + if (!osn_pfeaget(&pfdata, dpni->dpni_ifnam, rdea)) { error("Can't read \"%s\" e/n addr!", dpni->dpni_ifnam); /* Proceed as if set won, sigh */ } else { diff --git a/src/osdnet.c b/src/osdnet.c index 670a10e..ca78651 100644 --- a/src/osdnet.c +++ b/src/osdnet.c @@ -42,6 +42,11 @@ # error "A bridge is useless without a TAP device... configuration error!" #endif +#if HAVE_LIBVDEPLUG_H && HAVE_LIBVDEPLUG +# include +# define KLH10_NET_VDE 1 +#endif + /* Local predeclarations */ struct ifent *osn_iflookup(char *ifnam); @@ -50,13 +55,13 @@ static struct ifent *osn_iftab_addaddress(char *name, struct sockaddr *addr, str #if KLH10_NET_PCAP static void osn_pfinit_pcap(struct pfdata *pfdata, struct osnpf *osnpf, void *pfarg); static ssize_t osn_pfread_pcap(struct pfdata *pfdata, void *buf, size_t nbytes); -static int osn_pfwrite_pcap(struct pfdata *pfdata, const void *buf, size_t nbytes); +static ssize_t osn_pfwrite_pcap(struct pfdata *pfdata, const void *buf, size_t nbytes); #endif /* KLH10_NET_PCAP */ #if KLH10_NET_TUN || KLH10_NET_TAP static void osn_pfinit_tuntap(struct pfdata *pfdata, struct osnpf *osnpf, void *pfarg); static void osn_pfdeinit_tuntap(struct pfdata *pfdata, struct osnpf *osnpf); static ssize_t osn_pfread_fd(struct pfdata *pfdata, void *buf, size_t nbytes); -static int osn_pfwrite_fd(struct pfdata *pfdata, const void *buf, size_t nbytes); +static ssize_t osn_pfwrite_fd(struct pfdata *pfdata, const void *buf, size_t nbytes); #endif /* TUN || TAP */ struct tuntap_context; @@ -66,6 +71,15 @@ void tap_bridge_close(struct tuntap_context *tt_ctx); #endif /* KLH10_NET_BRIDGE */ static void osn_iff_up(int s, char *ifname); static int pfopen_create(char *basename, struct tuntap_context *tt_ctx, struct osnpf *osnpf); +#if KLH10_NET_VDE +static void osn_pfinit_vde(struct pfdata *pfdata, struct osnpf *osnpf, void *pfarg); +static void osn_pfdeinit_vde(struct pfdata *pfdata, struct osnpf *osnpf); +static ssize_t osn_pfread_vde(struct pfdata *pfdata, void *buf, size_t nbytes); +static ssize_t osn_pfwrite_vde(struct pfdata *pfdata, const void *buf, size_t nbytes); +#endif /* KLH10_NET_VDE */ +#if KLH10_NET_TUN || KLH10_NET_TAP || KLH10_NET_VDE +static void osn_virt_ether(struct pfdata *pfdata, struct osnpf *osnpf); +#endif /* TUN || TAP || VDE */ /* Get a socket descriptor suitable for general net interface examination and manipulation; this is not necessarily suitable for @@ -859,12 +873,31 @@ static struct eth_addr emhost_ea = /* Emulated host ether addr for tap */ /* OSN_PFEAGET - get physical ethernet address for an open packetfilter FD. * * Also not well documented, but generally easier to perform. -*/ + */ int -osn_pfeaget(int pfs, /* Packetfilter socket or FD */ +osn_pfeaget(struct pfdata *pfdata, /* Packetfilter data */ char *ifnam, /* Interface name (sometimes needed) */ unsigned char *eap) /* Where to write ether address */ { + int fd = pfdata->pf_fd; + + if (pfdata->pf_meth == PF_METH_TAP || + pfdata->pf_meth == PF_METH_VDE) { + + /* If we do tap(4) + bridge(4), the ether address of the tap is wholly + * irrelevant, it is on the other side of the "wire". + * Our own address is something we can make up completely. + */ + if (emhost_ea.ea_octets[5] == 0xFF) { + time_t t = time(NULL); + emhost_ea.ea_octets[5] = t & 0xFE; + emhost_ea.ea_octets[4] = (t >> 8) & 0xFF; + emhost_ea.ea_octets[3] = (t >> 16) & 0xFF; + } + ea_set(eap, &emhost_ea); /* Return the ether address */ + + return TRUE; + } #if KLH10_NET_NIT /* SunOS/Solaris: The EA apparently can't be found until after the PF FD @@ -904,18 +937,6 @@ osn_pfeaget(int pfs, /* Packetfilter socket or FD */ } ea_set(eap, endp.end_addr); -#elif KLH10_NET_TAP - /* If we do tap(4) + bridge(4), the ether address of the tap is wholly - * irrelevant, it is on the other side of the "wire". - * Our own address is something we can make up completely. - */ - if (emhost_ea.ea_octets[5] == 0xFF) { - time_t t = time(NULL); - emhost_ea.ea_octets[5] = t & 0xFE; - emhost_ea.ea_octets[4] = (t >> 8) & 0xFF; - emhost_ea.ea_octets[3] = (t >> 16) & 0xFF; - } - ea_set(eap, &emhost_ea); /* Return the ether address */ #elif (KLH10_NET_BPF && !CENV_SYS_NETBSD && !CENV_SYS_FREEBSD) /* NetBSD no longer seems to support this (on bpf) */ struct ifreq ifr; @@ -1311,7 +1332,14 @@ osn_pfinit(struct pfdata *pfdata, struct osnpf *osnpf, void *pfarg) return osn_pfinit_pcap(pfdata, osnpf, pfarg); } #endif /* KLH10_NET_PCAP */ +#if KLH10_NET_VDE + if (!method[0] || !strcmp(method, "vde")) { + pfdata->pf_meth = PF_METH_VDE; + return osn_pfinit_vde(pfdata, osnpf, pfarg); + } +#endif /* KLH10_NET_VDE */ + esfatal(1, "Interface method \"%s\" not supported", method); } ssize_t @@ -1524,7 +1552,7 @@ tryagain: * Expect a full ethernet frame including link-layer header. * returns the number of bytes written. */ -int +ssize_t osn_pfwrite_pcap(struct pfdata *pfdata, const void *buf, size_t nbytes) { //if (DP_DBGFLG) @@ -1533,6 +1561,7 @@ osn_pfwrite_pcap(struct pfdata *pfdata, const void *buf, size_t nbytes) } #endif /* KLH10_NET_PCAP */ + #if KLH10_NET_TUN || KLH10_NET_TAP /* Adapted from DEC's pfopen.c - doing it ourselves here because pfopen(3) @@ -1682,51 +1711,22 @@ The ifra_mask field is ignored (left as-is or zeroed if new) unless void osn_pfinit_tuntap(struct pfdata *pfdata, struct osnpf *osnpf, void *arg) { - int allowextern = TRUE; /* For now, always try for external access */ int fd; struct ifreq ifr; char *ifnam = osnpf->osnpf_ifnam; /* alias for the persisting copy */ char ipb1[OSN_IPSTRSIZ]; char ipb2[OSN_IPSTRSIZ]; - struct ifent *ife = NULL; /* Native host's default IP interface if one */ struct in_addr iplocal; /* TUN ifc address at hardware OS end */ struct in_addr ipremote; /* Address at remote (emulated guest) end */ - static unsigned char ipremset[4] = { 192, 168, 0, 44}; + char *basename = ""; int s; - /* Remote address is always that of emulated machine */ - ipremote = osnpf->osnpf_ip.ia_addr; - iplocal = osnpf->osnpf_tun.ia_addr; strncpy(tt_ctx.saved_ifnam, osnpf->osnpf_ifnam, IFNAM_LEN); if (DP_DBGFLG) dbprint("Opening %s device", pfdata->pf_meth == PF_METH_TUN ? "TUN" : "TAP"); - /* Local address can be set explicitly if we plan to do full IP - masquerading. */ - if (memcmp((char *)&iplocal, "\0\0\0\0", IP_ADRSIZ) == 0) { - /* Local address is that of hardware machine if we want to permit - external access. If not, it doesn't matter (and may not even - exist, if there is no hardware interface) - */ - if (allowextern) { - if ((ife = osn_ipdefault())) { - iplocal = ife->ife_ipia; - } else { - error("Cannot find default IP interface for host"); - allowextern = FALSE; - } - } - if (!allowextern) { - /* Make up bogus IP address for internal use */ - memcpy((char *)&iplocal, ipremset, 4); - } - osnpf->osnpf_tun.ia_addr = iplocal; - } - - char *basename = ""; - switch (pfdata->pf_meth) { case PF_METH_TUN: pfdata->pf_ip4_only = TRUE; @@ -1740,6 +1740,12 @@ osn_pfinit_tuntap(struct pfdata *pfdata, struct osnpf *osnpf, void *arg) esfatal(0, "pf_meth value %d invalid", pfdata->pf_meth); } + osn_virt_ether(pfdata, osnpf); + + /* Remote address is always that of emulated machine */ + ipremote = osnpf->osnpf_ip.ia_addr; + iplocal = osnpf->osnpf_tun.ia_addr; + fd = pfopen(basename, &tt_ctx, osnpf); if (fd < 0) { esfatal(0, "Couldn't open tunnel device %s", basename); @@ -1870,79 +1876,6 @@ osn_pfinit_tuntap(struct pfdata *pfdata, struct osnpf *osnpf, void *arg) osn_iff_up(s, ifnam); close(s); - /* Now optionally determine ethernet address. - This amounts to what if anything we should put in the native - host's ARP tables. - - If we only intend to use the net between the virtual host and - its hardware host, then no ARP hackery is needed. - - However, if the intent is to allow traffic between the virtual - host and other machines on the LAN or Internet, then an ARP - entry is required. It must advertise the virtual host's IP - address, using one of the hardware host's ethernet addresses - so any packets on the LAN for the virtual host will at least - wind up arriving at the hardware host it's running on. - */ - - /* Simple method is to get the ifconf table and scan it to find the - first interface with both an IP and ethernet address given. - Non-4.4BSD systems may not provide the latter datum, but if - we're using TUN then we almost certainly also have the good stuff - in ifconf. - */ - - /* - * Now get our fresh new virtual interface's ethernet address. - * Basically, we can make it up. - */ - if (!pfdata->pf_ip4_only) { - /* If we do tap(4) + bridge(4), the ether address of the tap is wholly - * irrelevant, it is on the other side of the "wire". - * Our own address is something we can make up completely. - */ - if (emhost_ea.ea_octets[5] == 0xFF) { - time_t t = time(NULL); - emhost_ea.ea_octets[5] = t & 0xFE; - emhost_ea.ea_octets[4] = (t >> 8) & 0xFF; - emhost_ea.ea_octets[3] = (t >> 16) & 0xFF; - } - ea_set(&osnpf->osnpf_ea, &emhost_ea); /* Return the ether address */ - - struct ifent *tap_ife = osn_ifcreate(ifnam); - if (tap_ife) { - tap_ife->ife_flags = IFF_UP; - ea_set(tap_ife->ife_ea, (unsigned char *)&osnpf->osnpf_ea); - tap_ife->ife_gotea = TRUE; - - if (DP_DBGFLG) { - dbprintln("Entered 10-side of TAP into table:"); - osn_iftab_show(stdout, &iftab[0], iftab_nifs); - } - } - } - - if (allowextern && ife) { - /* Need to determine ether addr of our default interface, then - publish an ARP entry mapping the virtual host to the same - ether addr. - */ - - /* Use emhost_ea as set up above */ - } else { - /* ARP hackery will be handled by IP masquerading and packet forwarding. */ -#if 1 /*OSN_USE_IPONLY*/ /* TOPS-20 does not like NI20 with made up address? */ - /* Assume no useful ether addr for tun interface. */ - ea_clr((char *)&osnpf->osnpf_ea); -#else - /* Assign requested address to tap interface or get kernel assigned one. */ - if (memcmp((char *)&osnpf->osnpf_ea, "\0\0\0\0\0\0", ETHER_ADRSIZ) == 0) { - osn_ifeaget(-1, ifnam, (unsigned char *)&osnpf->osnpf_ea, NULL); - } - else { - osn_ifeaset(-1, ifnam, (unsigned char *)&osnpf->osnpf_ea); - } -#endif - } - pfdata->pf_fd = fd; pfdata->pf_handle = &tt_ctx; pfdata->pf_can_filter = pfdata->pf_ip4_only; @@ -1994,7 +1927,7 @@ osn_pfread_fd(struct pfdata *pfdata, void *buf, size_t nbytes) * returns the number of bytes written. */ static inline -int +ssize_t osn_pfwrite_fd(struct pfdata *pfdata, const void *buf, size_t nbytes) { return write(pfdata->pf_fd, buf, nbytes); @@ -2078,6 +2011,7 @@ pfopen_create(char *basename, struct tuntap_context *tt_ctx, struct osnpf *osnpf #endif /* CENV_SYS_NETBSD */ #endif /* KLH10_NET_TAP || KLH10_NET_TUN */ + #if KLH10_NET_BRIDGE #if (CENV_SYS_NETBSD || CENV_SYS_FREEBSD) @@ -2260,6 +2194,168 @@ static void osn_iff_up(int s, char *ifname) } } + +#if KLH10_NET_VDE + +static +void +osn_pfinit_vde(struct pfdata *pfdata, struct osnpf *osnpf, void *pfarg) +{ + struct vde_open_args voa; + char *devname = osnpf->osnpf_ifnam; + char errbuf[256]; + + memset(&voa, 0, sizeof(voa)); + + if (!(pfdata->pf_handle = (void*) vde_open(devname, "simh", &voa))) { + syserr(errno, "Can't open VDE device \"%s\"", devname); + } else { + pfdata->pf_fd = vde_datafd((VDECONN*)(pfdata->pf_handle)); + pfdata->pf_meth = PF_METH_VDE; + pfdata->pf_can_filter = FALSE; + pfdata->pf_ip4_only = FALSE; + pfdata->pf_read = osn_pfread_vde; + pfdata->pf_write = osn_pfwrite_vde; + pfdata->pf_deinit = osn_pfdeinit_vde; + } + + osn_virt_ether(pfdata, osnpf); +} + +static +void +osn_pfdeinit_vde(struct pfdata *pfdata, struct osnpf *osnpf) +{ + vde_close(pfdata->pf_handle); +} + +static +ssize_t +osn_pfread_vde(struct pfdata *pfdata, void *buf, size_t nbytes) +{ + ssize_t len = vde_recv((VDECONN *)pfdata->pf_handle, buf, nbytes, 0); + + return len; +} + +static +ssize_t +osn_pfwrite_vde(struct pfdata *pfdata, const void *buf, size_t nbytes) +{ + ssize_t len = vde_send((VDECONN*)pfdata->pf_handle, buf, nbytes, 0); + + return len; +} +#endif /* KLH10_NET_VDE */ + + +#if KLH10_NET_TUN || KLH10_NET_TAP || KLH10_NET_VDE +/* + * Some common code for fully virtual ethernet interfaces, + * where we have to invent our own ethernet address. + */ +static +void +osn_virt_ether(struct pfdata *pfdata, struct osnpf *osnpf) +{ + static unsigned char ipremset[4] = { 192, 168, 0, 44}; + struct ifent *ife = NULL; /* Native host's default IP interface if one */ + + /* Local address can be set explicitly if we plan to do full IP + masquerading. */ + if (memcmp((char *)&osnpf->osnpf_tun.ia_addr, "\0\0\0\0", IP_ADRSIZ) == 0) { + int have_addr = TRUE; + /* Local address is that of hardware machine if we want to permit + external access. If not, it doesn't matter (and may not even + exist, if there is no hardware interface) + */ + if ((ife = osn_ipdefault())) { + osnpf->osnpf_tun.ia_addr = ife->ife_ipia; + } else { + error("Cannot find default IP interface for host"); + have_addr = FALSE; + } + if (!have_addr) { + /* Make up bogus IP address for internal use */ + memcpy((char *)&osnpf->osnpf_tun.ia_addr, ipremset, 4); + } + } + + /* Now optionally determine ethernet address. + This amounts to what if anything we should put in the native + host's ARP tables. + - If we only intend to use the net between the virtual host and + its hardware host, then no ARP hackery is needed. + - However, if the intent is to allow traffic between the virtual + host and other machines on the LAN or Internet, then an ARP + entry is required. It must advertise the virtual host's IP + address, using one of the hardware host's ethernet addresses + so any packets on the LAN for the virtual host will at least + wind up arriving at the hardware host it's running on. + */ + + /* Simple method is to get the ifconf table and scan it to find the + first interface with both an IP and ethernet address given. + Non-4.4BSD systems may not provide the latter datum, but if + we're using TUN then we almost certainly also have the good stuff + in ifconf. + */ + + /* + * Now get our fresh new virtual interface's ethernet address. + * Basically, we can make it up. + */ + if (!pfdata->pf_ip4_only) { + /* If we do tap(4) + bridge(4), the ether address of the tap is wholly + * irrelevant, it is on the other side of the "wire". + * Our own address is something we can make up completely. + */ + if (emhost_ea.ea_octets[5] == 0xFF) { + time_t t = time(NULL); + emhost_ea.ea_octets[5] = t & 0xFE; + emhost_ea.ea_octets[4] = (t >> 8) & 0xFF; + emhost_ea.ea_octets[3] = (t >> 16) & 0xFF; + } + ea_set(&osnpf->osnpf_ea, &emhost_ea); /* Return the ether address */ + + char *ifnam = osnpf->osnpf_ifnam; /* alias for the persisting copy */ + struct ifent *tap_ife = osn_ifcreate(ifnam); + if (tap_ife) { + tap_ife->ife_flags = IFF_UP; + ea_set(tap_ife->ife_ea, (unsigned char *)&osnpf->osnpf_ea); + tap_ife->ife_gotea = TRUE; + + if (DP_DBGFLG) { + dbprintln("Entered 10-side of %s into table:", ifnam); + osn_iftab_show(stdout, &iftab[0], iftab_nifs); + } + } + } + + if (ife) { + /* Need to determine ether addr of our default interface, then + publish an ARP entry mapping the virtual host to the same + ether addr. + */ + + /* Use emhost_ea as set up above */ + } else { + /* ARP hackery will be handled by IP masquerading and packet forwarding. */ +#if 1 /*OSN_USE_IPONLY*/ /* TOPS-20 does not like NI20 with made up address? */ + /* Assume no useful ether addr for tun interface. */ + ea_clr((char *)&osnpf->osnpf_ea); +#else + /* Assign requested address to tap interface or get kernel assigned one. */ + if (memcmp((char *)&osnpf->osnpf_ea, "\0\0\0\0\0\0", ETHER_ADRSIZ) == 0) { + osn_ifeaget(-1, ifnam, (unsigned char *)&osnpf->osnpf_ea, NULL); + } + else { + osn_ifeaset(-1, ifnam, (unsigned char *)&osnpf->osnpf_ea); + } +#endif + } +} +#endif /* TUN || TAP || VDE */ #if KLH10_NET_DLPI diff --git a/src/osdnet.h b/src/osdnet.h index 5001af8..f1b0239 100644 --- a/src/osdnet.h +++ b/src/osdnet.h @@ -286,7 +286,7 @@ struct pfdata; struct osnpf; typedef ssize_t (*osn_pfread_f)(struct pfdata *pfdata, void *buf, size_t nbytes); -typedef int (*osn_pfwrite_f)(struct pfdata *pfdata, const void *buf, size_t nbytes); +typedef ssize_t (*osn_pfwrite_f)(struct pfdata *pfdata, const void *buf, size_t nbytes); typedef void (*osn_pfdeinit_f)(struct pfdata *, struct osnpf *); /* @@ -308,6 +308,7 @@ struct pfdata { #define PF_METH_PCAP 1 #define PF_METH_TUN 2 #define PF_METH_TAP 3 +#define PF_METH_VDE 4 int osn_iftab_init(void); int osn_nifents(void); /* # of entries cached by osn_iftab_init */ @@ -318,7 +319,7 @@ void osn_iftab_show(FILE *f, struct ifent *ife, int nents); void osn_ifctab_show(FILE *f, struct ifconf *ifc); int osn_ifipget(int s, char *ifnam, unsigned char *ipa); int osn_ifnmget(int s, char *ifnam, unsigned char *ipa); -int osn_pfeaget(int s, char *ifnam, unsigned char *eap); +int osn_pfeaget(struct pfdata *, char *ifnam, unsigned char *eap); int osn_ifeaget2(char *ifnam, unsigned char *eap); #if !OSN_USE_IPONLY int osn_ifeaset(int s, char *ifnam, unsigned char *newpa);